C# EntityFramwork(Model First)使用要点

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:\学习实验区\EntityFramework\Log.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:\学习实验区\EntityFramework\Log1.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();
        }
posted @ 2017-12-26 21:32 qz奔跑的马 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2021 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值