zoukankan      html  css  js  c++  java
  • C# EntityFramwork(Model First)使用要点

    本文介绍EntityFramework使用方法

    1. Entity Framework的注意点

      由于安装和操作的细节讲起来很琐碎,这部分只罗列出难点,其他细节请自行查阅
      安装细节
          Pluralize or singularize generated object names(确定所生成对象名称的单复数形式)复选框
              此复选框的意思是,将数据库中的复数表名映射成单数形式的实体类,如果有一对多,多对多关系的表会将属性名以复数形式显示,增加程序可读性
          Include foreign key columns in the model (在模型中包含外键)复选框
              将数据库外键关系映射到实体模型中
          Import selected stored procedures and functions into entity model (将所选存储过程和函数导入到实体模型中) 
              将存储过程和函数导入到实体模型
      操作CRUD
          要先实例化上下文类
          using(var ctx = new SchoolDBEntities()) 
          {
              // 进行增删改查
          }
      
    2. 支持的查询方法

      支持的查询数据的方式有
          LINQ to Entites
              linq方法
                  var linq = ctx.Students.Where(s => s.StudentID > 2);
              linq查询语句
                  var linq = from item in ctx.Students select item; 
          Entity SQL
              此法不建议使用
          Native SQL
              var tempResult = ctx.Students.SqlQuery("select * from Student");
              注意:sql语句返回的列不能改名,否则报错
              返回值类型的写法
                  string result = db.Database.SqlQuery<string>("select studentname from Student where studentid = 1").FirstOrDefault<string>();
              执行非查询sql的写法
                  int noOfRowUpdated = ctx.Database.ExecuteSqlCommand("Update student set studentname ='changed student by command' where studentid=1");
      
    3. ef表示体包含的方法

      ctx.Students表实体中除了上面讲到的SqlQuery方法外还包括如下方法
          Add方法
              db.Students.Add(student); // 添加一条记录到实体框架中
          AsNoTracking方法
              如果查询的数据只是用来展示的,可以使用AsNoTracking方法将查询的数据和DbContext断开连接,提高查询效率
              var result = db.Students.Select(s => new
              {
                  s.StandardId,
                  s.StudentName
              }).AsNoTracking().ToList();
          Attach方法
              将一条Student数据附加到EF的跟踪图中就像数据是从数据库中读取的一样,其实数据库中并不存在
              var result = db.Students.Attach(st);
          Create方法
              创建一个空的Student实例,该实例没有附加到Students集合中
              var result = db.Students.Create();
          Find方法
              返回Students表中主键id是5的数据,与之关联的数据也会一起返回
              如果数据已经添加到上下文中,但是没有保存到数据库,使用Find同样有效
              var result = db.Students.Find(5);
          Include方法
              指定包含在结果集中的导航属性
                  var result = db.Students.Include("StudentAddress").ToList<Student>();
                  也可以传递lambda表达式
                  var result = db.Students.Include(s => s.Standard).ToList<Student>();
          Remove方法
              将提供的Student实例标记为已删除,改实例必须存在于上下文中
              var result = db.Students.Remove(st);
          SqlQuery带参数的用法    
              var result = db.Students.SqlQuery("select * from student where studentid = @p", new SqlParameter("@p", SqlDbType.Int) { Value = 5} ).Select(s => s.StudentName).ToList();
      
    4. DBEntityEntry对象

      ef实体框架中,可以通过数据实体获取数据实体相关的DBEntityEntry对象,从而得到数据实体的所有信息
      var student = db.Students.Find(1);
      student.StudentName = "yejiawei";
      var entry = db.Entry(student); // 获取数据实体的DBEntityEntry对象
      属性
          var result = entry.Entity.GetType().FullName; // 获取实体的名称
          var result = entry.State.ToString(); // 获取实体当前的状态
          IEnumerable<string> result = entry.CurrentValues.PropertyNames; // 获取实体的所有属性名
          var result = entry.OriginalValues["StudentName"]; // 获取实体属性修改之前的值
          var result = entry.CurrentValues["StudentName"]; // 获取实体属性修改之后的值
          可以手动设置实体的状态为 Added,Modified,Deleted
              entry.State = EntityState.Modified;
      方法
          Collection
              返回导航属性的集合,多对多关系
              var result = entry.Collection("Courses");
              可以继续使用 result.Load() 预载数据
              还可以继续使用 result.Query().Where(...) 进一步筛选数据
          ComplexProperty
              返回复杂属性的集合
          GetDatabaseValues
              返回跟踪的属性集合
              var result = entry.GetDatabaseValues();
          Property
              返回实体指定属性的所有信息组成的集合
              var result = entry.Property("StudentName");
          Reference
              返回导航属性的集合,一对多或者多对一关系
              var result = entry.Reference("StudentAddress");
              可以继续使用 result.Load() 预载数据
              还可以继续使用 result.Query().Where(...) 进一步筛选数据
          Reload方法
              entry.Reload();
              调用此方法,之前的所有更改都无效,全部和数据库同步,并且当前状态是Unchanged
      
    5. 追踪变化

      在ef上下文的生命周期中,ef会自动追踪加载进来的实体,可以通过ChangeTracker来获取所有被上下文追踪的实体
      每一个实体都必须包含一个主键,如果没有主键ef是不会追踪的
      var student = db.Students.Find(1);
      属性
          var result = db.ChangeTracker.Entries(); // 获取所有被上下文追踪的实体
          var result = db.ChangeTracker.Entries().Count(); // 获取所有被上下文追踪的实体的个数
          var result = db.ChangeTracker.Entries(); // 获取当前上下文中的所有DBEntityEntry对象对象
      
    6. 维持Entity Framework的实体场景

      连接场景
          数据检索和增删改都在一个上下文里面操作
          在连接场景中完成增删改查是非常容易的,因为上下文自动跟踪实体的状态,AutoDetectChangesEnabled默认是true
          添加操作
              定义一个引用类型比较类
                  public class StudentComparer : IEqualityComparer<Student>
                  {
                      public bool Equals(Student x, Student y)
                      {
                          if (x.StudentName.ToLower() == y.StudentName.ToLower())
                          {
                              return true;
                          }
                          return false;
                      }
                      public int GetHashCode(Student obj)
                      {
                          return obj.GetHashCode();
                      }
                  }
              新增操作
                  if(!db.Students.ToList().Contains(new Student() { StudentName = "yejiawei" }, new StudentComparer()))
                  {
                      db.Students.Add(new Student() { StudentName = "yejiawei" });
                      db.SaveChanges();
                      return Ok("添加成功");
                  }
                  return Ok("已经存在了");
          修改操作
              Student StudetUpdate = db.Students.ToList().Where(s => s.StudentName == "yejiawei").FirstOrDefault();
              StudetUpdate.StudentName = "叶家伟";
              db.SaveChanges();
          删除操作
              db.Students.Remove(db.Students.ToList().ElementAt(0));
              db.SaveChanges();
      断开连接场景
          数据检索和增删改在不同的上下文里面操作
          在断开连接场景中,我们需要附加实体到新的上下文中,并且要指定状态
          Add方法,会自动的将实体块附加到新的上下文中并且自动的修改状态为Added,也就是说你调用Add方法,管他啥玩意儿都可以成功
          Attach方法,调用Attach方法会将实体块添加到上下文中,但是状态还是Unchanged,需要手动设置
              var test = db.Students.Attach(new Student() { StudentName = "yejiawei" });
              db.Entry(test).State = EntityState.Added;
              var result = db.Entry(test).State.ToString();
              db.SaveChanges();
          单个的实体
              单个的实体,直接更改状态,然后保存即可
              var test = new Student() { StudentName = "叶家伟" };
              db.Entry(test).State = EntityState.Added;
              var result = db.Entry(test).State.ToString();
              db.SaveChanges();
      
    7. Entity Framework并发问题

      在ef中可以根据数据库timestamp类型的列来处理并发问题,timestamp数据会在插入和更新时自动更改并且是唯一的
      数据库中timestamp类型的值是自动添加的
      然后,需要在ef中将timestamp列属性中的Concurency Mode设置为Fixed
      此时,ef在执行更新操作会将timestamp列设置成where的字句,来判断操作的数据有没有并发操作,如果发生了并发操作,那么会抛出DbUpdateConcurrencyException错误
          Student test1 = null;
          using(var context1 = new SchoolDBEntities())
          {
              context1.Configuration.ProxyCreationEnabled = false; // 查询的时候是否包含导航属性
              test1 = context1.Students.Where(s => s.StudentID == 9).Single();
              test1.StudentName = "小王";
              try
              {
                  context1.Entry(test1).State = EntityState.Modified;
                  context1.SaveChanges();
              }
              catch (DbUpdateConcurrencyException ex)
              {
                  var t = ex;
              }
          }
      
    8. 执行存储过程

      如下的存储过程导入到ef中
          ALTER PROCEDURE [dbo].[GetCoursesByStudentId]
              -- Add the parameters for the stored procedure here
              @StudentId int = null
          AS
          BEGIN
              -- SET NOCOUNT ON added to prevent extra result sets from
              -- interfering with SELECT statements.
              SET NOCOUNT ON;
              -- Insert statements for procedure here
          select c.courseid, c.coursename,c.Location, c.TeacherId
          from student s left outer join studentcourse sc on sc.studentid = s.studentid left outer join course c on c.courseid = sc.courseid
          where s.studentid = @StudentId
          END
      然后直接当做函数执行就可以了
          var courses = db.GetCoursesByStudentId(1);
      可以将更新,删除,新增的存储过程映射到实体上面,从而重写ef默认的增删改的方法
          存储过程中,新增的时候执行SELECT SCOPE_IDENTITY() AS StudentId,可以返回新增数据的id
      
    9. 在ef中使用枚举

      数据库中可以使用一些特殊的整数值,来表示数据的特殊含义,为了是代码的可读性更强,使用枚举来代替这些整数值
      首先,将实体中的一个属性列,Convert to Enum
      然后,在弹出的选择框中,输入枚举类型的名字,也就是你在写代码时要用到的枚举名称
      其次,在成员列表中,指定成员的名字,和成员对应的整数值
      现在,你可以使用你定义的枚举了
          var type = TeacherType.Guest;
      
    10. 在ef中使用表值函数

      首先要在数据库中添加一个表值函数
          ALTER FUNCTION [dbo].[GetCourseListByStudentID] 
          (	
              -- Add the parameters for the function here
              @studentID int
          )
          RETURNS TABLE 
          AS
          RETURN 
          (
              -- Add the SELECT statement with parameter references here
              select c.courseid, c.coursename,c.Location, c.TeacherId
              from student s left outer join studentcourse sc on sc.studentid = s.studentid left outer join course c on c.courseid = sc.courseid
              where s.studentid = @studentID
          )
      然后将这个表值函数导入到ef中,可以在模型浏览器中修改函数,然后在ef中使用表值函数,如下
          var result = db.GetCoursesByStudentId(1).Select(s => s);
      
    11. Local属性

      可以通过Local属性访问当前被追踪的实体,被标记为Deleted的实体不可以通过Local访问
      db.Students.Load(); // 预载,免去写ToList
      db.Students.Add(new Student() { StudentName = "New Student" });
      return Ok(db.Students.Local);
      
    12. 预先加载

      使用Include,表示加载导航属性
          (from s in context.Students.Include("Standard")
          where s.StudentName == "Student1"
          select s).FirstOrDefault<Student>();
      
    13. 懒加载

      当实体中包含包含复杂属性,只有当访问这个复杂属性的时候才会去查询
      如果要为整个上下文关闭懒加载,可以在上下文的类中设置
          public SchoolDBEntities(): base("name=SchoolDBEntities")
          {
              this.Configuration.LazyLoadingEnabled = false;
          }
      如果想只关闭某一个属性的懒加载,可以导实体的类中设置
          public virtual StudentAddress StudentAddress { get; set; }将这个virtual关键字去掉即可
      
    14. 异步查询和保存

      异步查询
          使用async标记此方法是异步的
          public async Task<Student> GetStudents()
          {
              Student result = null;
              using (var context = new SchoolDBEntities())
              {
                  context.Configuration.ProxyCreationEnabled = false;
                  // 使用await,可以释放当前线程从而可以执行其他代码,避免阻塞程序
                  result = await context.Students.Where(s => s.StudentID == 10).FirstOrDefaultAsync<Student>();
              }
              return result;
          }
      异步保存
          public async Task<int> Get()
          {
              using (var context = new SchoolDBEntities())
              {
                  var test = context.Courses.Attach(new Course() { CourseName = "数学" });
                  context.Entry(test).State = EntityState.Added;
                  return await context.SaveChangesAsync();
              }
          }
      
    15. 日志的记录

      第一种方式,使用默认配置
          在web.config配置文件中的entityFramework节点下添加
          <interceptors>
              <interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
                  <parameters>
                  <parameter value="F:学习实验区EntityFrameworkLog.txt"/>
                  <parameter value="true" type="System.Boolean"/>
                  </parameters>
              </interceptor>
          </interceptors>
          即可自动的记录ef做的所有操作
      第二种方式,自定义日志记录
          using (var context = new SchoolDBEntities())
          {
              // 使用Log方法即可自动实现记录日志,然后追加到日志文件中
              context.Database.Log = (string message) =>
              {
                  byte[] bts = Encoding.UTF8.GetBytes(message);
                  using (FileStream fwrite = File.OpenWrite(@"F:学习实验区EntityFrameworkLog1.txt"))
                  {
                      //fwrite.Write(bts, 0, bts.Length);
                      fwrite.Position = fwrite.Length;
                      fwrite.Write(bts, 0, bts.Length);
                  }
              };
              var test = context.Courses.Attach(new Course() { CourseName = "数学" });
              context.Entry(test).State = EntityState.Added;
              return await context.SaveChangesAsync();
          }
      
    16. 事务提交和回滚

      ef默认的操作都是事务处理的,但是我们可以将ef多个操作再用一个事务封装起来,方便回滚
          using(DbContextTransaction dbTran = context.Database.BeginTransaction())
          {
              try
              {
                  var test = context.Courses.Attach(new Course() { CourseName = "333" });
                  context.Entry(test).State = EntityState.Added;
                  await context.SaveChangesAsync();
                  dbTran.Commit();
                  return 1;
              }catch(Exception ex)
              {
                  dbTran.Rollback();
                  return 0;
              }
          }
      
    17. AddRange和RemoveRange

      AddRange批量增加
          List<Course> list = new List<Course>()
          {
              new Course() { CourseName = "yuwen" },
              new Course() { CourseName = "shuxue" },
              new Course() { CourseName = "yinyu" }
          };
          using (var context = new SchoolDBEntities())
          {
              context.Courses.AddRange(list);
              return await context.SaveChangesAsync();
          }
      RemoveRange批量删除
          using (var context = new SchoolDBEntities())
          {
              var items = context.Courses.Where(
                  s => s.CourseName == "yuwen" || s.CourseName == "shuxue" || s.CourseName == "yinyu"
              ).AsEnumerable<Course>();
              context.Courses.RemoveRange(items);;
              return await context.SaveChangesAsync();
          }
      
  • 相关阅读:
    ACM学习历程——POJ3321 Apple Tree(搜索,线段树)
    ACM学习历程——NOJ1113 Game I(贪心 || 线段树)
    ACM学习历程——HDU5202 Rikka with string(dfs,回文字符串)
    ACM学习历程——hihoCoder挑战赛10A 01串(策略)
    ACM学习历程——POJ3468 A Simple Problem with Integers(线段树)
    ACM学习历程——POJ1260 Pearls(动态规划)
    java入门了解09
    java入门了解08
    java入门了解之快捷键
    java入门了解07
  • 原文地址:https://www.cnblogs.com/ye-hcj/p/8120116.html
Copyright © 2011-2022 走看看