一、关于 DbContext

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

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

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

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

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

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

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

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

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

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

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

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

  在 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();

  运行程序:

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

  可以看到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()等方法时),也会执行查询操作。

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

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

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

    ④ 重新加载实体数据时

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

  2、  管理更新操作

       EF 中的变动更新操作

     using (var context = new StudentInfoDBModel())
     {
         Student stu = context.Student.First();   // 获取 Student 表中的第一条记录
         Console.WriteLine($"姓名:{stu.Name} \t 年龄:{stu.Age}");
 
         stu.Age = 18;   // 更新年龄
         context.SaveChanges();  // SaveChanges()方法保存变更到数据控中
         Console.WriteLine($"姓名:{stu.Name} \t 年龄:{stu.Age}");
 
         //重载
         context.Entry(stu).Reload();
         stu = context.Student.First();
         Console.WriteLine($"姓名:{stu.Name} \t 年龄:{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 提供连接数据库的支持。

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

     这行代码获取了 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();

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

     我们还可以通过 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} \n数据库:{dbName} \n服务器:{serverName} \n状态:{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();

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

     不同的实现方式,预期的效果相同,相比之下 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 语句。

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

  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() 方法是基于主键进行查找的。如果复合式索引主键,就需要依次传入所有主键字段。否则:

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

    注意: 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();

 

 

相关文章: