Entity Framework 实体数据模型——数据处理

 一、关于 DbContext

  在 Entity Framework 环境下,程序通过实体数据模型与底层数据库进行沟通。

  数据模型是一个继承自 DbContext 的自定义类文件,也就是context。

  无论是使用 “EF 设计器” 还是 “Code First”,都必须经由 context 进行数据库的互动操作。

  下面通过实例来介绍下 DbContext 对象。

  通过已有的数据库,选择 “来自数据库的Code First” 模式来建立实体数据库模型实例。

   

     通过模型类 StudentInfoDBModel 可以看出,它继承自 DbContext 并在 EF 中负责几项关键任务。

     对 DbSet<T> 类对象的管理,每一个DbSet<TEntity> 对象都是封装特定数据的实体集(Entity Set)对象,

  实体集映射到底层数据库的特定数据表。

  在 EF 环境中对任何数据库执行的存取操作,都会生成对应到连接数据库的SQL语句,

  而 EF 负责将语句放到数据库中执行并返回执行结果。

   

  在 Program.cs 类的 Main 方法中添加如下代码:

  using (var context = new StudentInfoDBModel())  // DbContext 对象会占用资源,通常我们会使用 using 语句来创建对象实例
  {
      IEnumerable<Student> stus = from stu in context.Student select stu;
      string sql = stus.ToString();
      Console.WriteLine($"{sql}");
  }
  Console.ReadLine();

  运行程序:

  

  可以看到EF生成了对应的SQL语句。

  当我们想要读取到对应的数据时,这段SQL将被传递给数据库去进行执行,并返回相应的执行结果。

  然后通过 Context 对象将返回的数据转换成对应的可枚举的 IEnumerable<Student> 集合对象。

 static void Main(string[] args)
 {
     using (var context = new StudentInfoDBModel())  
     {
         IEnumerable<Student> stus = from stu in context.Student select stu;
         string sql = stus.ToString();
         Console.WriteLine($"{sql}");
         foreach (var stu in stus)
         {
             Console.WriteLine(stu.Name);
         }
     }
     Console.ReadLine();
 }

  EF 除了负责产生SQL语句外,还会管理SQL的传送与返回的数据封装,因此我们只需处理LINQ

  与数据对象即可。因此避免了因编写SQL而导致的错误和安全问题。

  1、 连接与查询

    EF 的数据库连接由 DbContext 自行维护,当一个查询开始执行时,连接便会适时的打开,直到查询结束再自动关闭连接。

    连接是相对耗费资源的操作,因此我们需要了解下 EF 是在何时进行SQL语句传递的,以避免不必要的性能耗费。那么,

    DbContext 会在何时去执行查询操作呢?

    ① 进行遍历对象时

      比如上面的 LINQ 语句,当程序执行到这段LINQ时,查询操作其实并不会马上执行,而是在执行 foreach 语句时,

      查询操作才会执行。

    ② 类型转换时

        LINQ 语句返回的通常是 IEnumerable 或 IQueryable 对象,有时我们会将其转换成 List 类的对象,在进行类型装换

        的过程中(比如调用 ToList()、ToArray()、ToDictionary()等方法时),也会执行查询操作。

      

    ③ 调用任何针对LINQ结果执行的方法时,如调用 Count()、First()等方法时。

      

    ④ 重新加载实体数据时

      比如在进行更新操作以后,想要获取最新的数据内容时,可以通过重载来达到目的,这个时候查询就会再被执行一次。

  2、  管理更新操作

       EF 中的变动更新操作

     using (var context = new StudentInfoDBModel())
     {
         Student stu = context.Student.First();   // 获取 Student 表中的第一条记录
         Console.WriteLine($"姓名:{stu.Name} 	 年龄:{stu.Age}");
 
         stu.Age = 18;   // 更新年龄
         context.SaveChanges();  // SaveChanges()方法保存变更到数据控中
         Console.WriteLine($"姓名:{stu.Name} 	 年龄:{stu.Age}");
 
         //重载
         context.Entry(stu).Reload();
         stu = context.Student.First();
         Console.WriteLine($"姓名:{stu.Name} 	 年龄:{stu.Age}");
     }
     Console.ReadLine();

    可以看到,在更新年龄之后,程序调用了 context.SaveChanges(); 方法将更新后的数据保存到了数据库中。

    这么一来数据对象就发生了改变,如果想要实时获取更新后的数据内容,那么就需要进行重载 DbContext 对象。

    以上示例中调用的 First()方法 和 Reload() 重载方法都会导致连接的建立,并且执行底层数据库的查询操作。

  3、  DbContext 对象的生命周期

    DbContext 对象会占用资源,通常我们会使用 using 语句来创建对象实例,当 using 区块结束时,对象的生命

    周期也就相应的结束了,并且会释放其占用的资源。

    如果直接创建 DbContext 对象,就必须调用 Dispose 方法;如果不调用该方法的话,就只能等待系统的资源

    回收机制去进行资源释放了。

  4、 管理与操作数据库(Database属性)

    DbContext 有一个 Database 属性,它会返回一个 System.Data.Entity.Database 对象,为 DbContext 提供连接数据库的支持。

    

     这行代码获取了 Database 对象,我们可以通过 db 变量来执行有关数据库的操作。

    Database 中有一组 log 属性支持获取 DbContext 生成的 SQL 记录。我们可以通过该属性来追踪程序运行过程中所使用的SQL。

     using (var context = new StudentInfoDBModel()) 
     {
         context.Database.Log = Console.WriteLine;  // 通过该配置,可以跟踪程序执行中所用到的 SQL 语句
         IEnumerable<Student> stus = from stu in context.Student select stu;
         foreach (var item in stus)
         {
             Console.WriteLine(item.Name);
         }
     }
     Console.ReadLine();

    

     我们还可以通过 context.Database.Connection 对象来获取连接的相关信息。

      using (var context = new StudentInfoDBModel()) 
      {
          DbConnection dbCon = context.Database.Connection;   // 获取数据库的连接对象
          string conn = dbCon.ConnectionString;               // 获取数据库的连接字符串
          string dbName = dbCon.Database;                     // 获取数据库名称
          string serverName = dbCon.DataSource;               // 获取要连接到数据库服务器的名称。
          System.Data.ConnectionState state = dbCon.State;    // 获取连接状态

          Console.WriteLine($"连接字符串:{conn} 
数据库:{dbName} 
服务器:{serverName} 
状态:{state}");
      }
      Console.ReadLine();

  5、ObjectContext

     在 EF 4.1 之前,支持数据库操作功能实现的类必须继承 ObjectContext,直到 EF 4.1 之后更易于使用的 DbConext

    才被公布了出来,成为了默认的继承类,ObjectContext 可视为轻量级版的 DbContext,而 DbContext 依然实现了 

    IObjectContextAdapter.ObjectContext 属性,以返回底层的 ObjectContext 对象。

     System.Data.Entity.Core.Objects.ObjectContext objDbCont = (context as System.Data.Entity.Infrastructure.IObjectContextAdapter).ObjectContext;

    DbContext 简化了 ObjectContext 的用法:

     using (var context = new StudentInfoDBModel()) 
     {
         // ObjectContext
         ObjectContext objDbCont = (context as IObjectContextAdapter).ObjectContext;
         ObjectSet<Student> objStu = objDbCont.CreateObjectSet<Student>();
         var oq = (ObjectQuery)objStu.Where(s => s.Age > 16);
         Console.WriteLine(oq.ToTraceString());
         foreach (Student item in oq)
         {
             Console.WriteLine(item.Name);
         }

         // DbContext
         var stu = context.Student.Where(s => s.Age > 16);
         Console.WriteLine(stu.ToString());
         foreach (var st in stu)
         {
             Console.WriteLine(st.Name);
         }
     }
     Console.ReadLine();

    

     不同的实现方式,预期的效果相同,相比之下 DbContext 更为简洁。

二、关于 DbSet

  1、DbSet

    DbSet 也是 EF 重要的类之一,表示实体模型中的特定实体对象集合。

    当 DbContext 将查询送入数据库并获取返回的数据时,这些数据将被转换为对应的实体类对象,然后存储在 

    DbSet 中,并通过 DbContext 对象属性公开,以供程序提取。

    DbSet 实现了 IEnumerable 接口,所以支持 LINQ 语法和方法。程序通过 LINQ 生成原始的 SQL 语句,并

    由 DbContext 传送至数据库执行。我们可以通过 ToString() 方法来获取对应的 SQL 语句。

     using (var context = new StudentInfoDBModel())
     {
         string sql = context.Student.ToString();
         Console.WriteLine(sql);
     }
     Console.ReadLine();

    上面的 context.Student 会返回 DbSet<Student> 对象,其中封装了来自 Student 表的内容,因此我们可以通过

    ToString() 的方法来获取到对应的 SQL 语句。

    

  2、查询数据(Find() 方法)

    Find() 方法必须指定所有主键值才能正确地执行。

     using (var context = new StudentInfoDBModel())
     {
         Student stu = context.Student.Find(5);
         if (stu != null)
             Console.WriteLine(stu.Name);
         else
             Console.WriteLine("不存在");
     }
     Console.ReadLine();

    Find(5) 表示查找 ID 字段值为 5 的一条数据。

    Find() 方法必须传入实体类中定义的所有主键属性才能正确的进行查找。

    也就是说 Find() 方法是基于主键进行查找的。如果复合式索引主键,就需要依次传入所有主键字段。否则:

    

    注意: Find() 方法查找数据的方式在性能方面要优于 LINQ 的方式。

  3、执行原始的 SQL 语句

     如果想要直接执行已有的 SQL 语句,可以通过调用 SqlQuery() 方法:

      using (var context = new StudentInfoDBModel())
      {
          SqlParameter sqlPara = new SqlParameter("Age", 16);
          var stus = context.Student.SqlQuery("select * from Student where Age > @Age", sqlPara);
          foreach (var stu in stus) 
          {
              Console.WriteLine(stu.Name);
          }
      }
      Console.ReadLine();

  4、数据变动与更新

       DbSet 支持对象的变动和更新。无论是修改或删除集合中的数据,还是往集合中添加数据,这些针对 DbSet 执行的操作,

    都会随着调用 SaveChange 方法同步更新到 DBContext 所连接的数据库中。

    添加对象需调用 Add() 方法:

     using (var context = new StudentInfoDBCModel())
     {
         Student sun = new Student
         {
             Name = "孙悟空",
             Age = 12,
             Sex = false,
             Hobby = "七十二变",
             Birthday = DateTime.Now
         };

         DbSet<Student> stus = context.Student;
         stus.Add(sun);
         int result = context.SaveChanges();
         Console.WriteLine(result);
 
         foreach (var stu in stus)
         {
             Console.WriteLine(stu.Name);
         }
     }
     Console.ReadLine();

    添加的数据量过多的话,可以通过调用 AddRange() 方法来实现:

     using (var context = new StudentInfoDBCModel())
     {
         List<Student> list = new List<Student>(){
             new Student
             {
                 Name = "孙悟空",
                 Age = 12,
                 Sex = false,
                 Hobby = "七十二变",
                 Birthday = DateTime.Now
             },
             new Student
             {
                 Name = "猪八戒",
                 Age = 12,
                 Sex = false,
                 Hobby = "三十六变",
                 Birthday = DateTime.Now
             }
         };
         DbSet<Student> stus = context.Student;
         stus.AddRange(list);
         int result = context.SaveChanges();
         Console.WriteLine(result);

         foreach (var stu in stus)
         {
             Console.WriteLine(stu.Name);
         }
     }
     Console.ReadLine();

    修改和添加都有了,还少个删除。删除调用的是 Remove() 或 RemoveRange() 方法:

    删除的操作是针对特定的数据对象,因此在删除之前必须先获取对象。

     using (var context = new StudentInfoDBCModel())
     {
         // Remove
         Student stud = context.Student.Find(1);
         if(stud != null)
         {
             context.Student.Remove(stud);
             context.SaveChanges();
         }
         else
         {
             Console.WriteLine("不存在!");
         }
 
         // RemoveRange
         IQueryable<Student> stus = context.Student.Where(s => s.Age > 6);
         context.Student.RemoveRange(stus);
         context.SaveChanges();
     }
     Console.ReadLine();
原文地址:https://www.cnblogs.com/LittleBai/p/14072975.html