EntityFramework 6学习笔记
-
来自数据库的EF设计器(DBFirst)
- 解释:根据已经设计好的数据库,来生成操作模型对象。
-
空的EF设计模型
- 解释:创建一个空的EDM实体数据模型工具,通过在EDMX文件中建立模型对象来生成数据库和操作对象。
-
空的CodeFirst模型
- 解释:通过代码建立实体对象模型,生成数据库。
-
来自数据库的CodeFirst
- 解释:根据已经设计好的数据库为模型,生成操作对象和数据库上下文。
-
EF的导航属性和跟踪管理机制
-
EF导航数据:外键被称为导航属性,映射出该表与导航属性表的关系。通过导航数据可以用来查询和延迟加载。
-
跟踪机制:通常都是通过调用SaveChanges方法把增加/修改/删除的数据提交到数据库。但是上下文是如何知道实体对象是增加、修改是通过EntityState的枚举值来判断的。
-
EntityState状态
-
Detached:对象存在,但未由对象服务跟踪。在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态;(已分离:上下文未跟踪该实体)
-
Unchanged:自对象加载到上下文中后,或自上次调用 System.Data.Objects.ObjectContext.SaveChanges() 方法后,此对象尚未经过修改;(保持不变:上下文正在跟踪实体,该实体存在于数据库中,并且其属性值未更改为数据库中的值)
-
Added:对象已添加到对象上下文,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法;(已添加:上下文正在跟踪实体,但数据库中尚不存在该实体)
-
Deleted:使用 System.Data.Objects.ObjectContext.DeleteObject(System.Object) 方法从对象上下文中删除了对象;(已删除:实体正在由上下文跟踪,并存在于数据库中,但在下次调用 SaveChanges 时已标记为要从数据库中删除)
-
Modified:对象已更改,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法。(已修改:实体正在由上下文跟踪,并存在于数据库中,并且其部分或全部属性值已修改)
-
https://docs.microsoft.com/zh-cn/ef/ef6/saving/change-tracking/entity-state?redirectedfrom=MSDN
-
Attach
- Attach:将一个处于Detached的Entity附加到上下文,而附加到上下文后的这一Entity的State为UnChanged。
- 在上面的程序中通过调用Attach()方法只能把对象添加到了对象上下文中(此时对象的状态已经是Unchanged的),如果执行附加实体的任何其他操作, 执行SaveChange()方法并不能真正添加到数据库中去,不会对数据库进行任何更改。 这是因为实体处于未更改状态。
-
-
插入或更新模式插入或更新模式
- 可以通过基于主键值的检查设置实体状态来实现此模式,通常将包含零键的实体作为新的和具有非零键的实体视为现有的。
- context.Entry(blog).State = blog.BlogId == 0 ? EntityState.Added :EntityState.Modified;
-
EF查询优化
-
查询优化
- 取消跟踪
- AsNoTracking()
- db.Configuration.AutoDetectChangesEnabled = false;
- 查询条件,带着变量(变量将会变成参数)效率提高
- 取消跟踪
-
修改优化
- 普通方式:先查找对象,在根据查找的对象修改对象,最后保存数据库
- 直接new一个字段,设置上下文状态,保存数据库。比普通方式少了一个查询操作。
-
-
/*查询状态跟踪*/ /* * 方式一 * AsNoTracking() */ var students= db.Students.ToList(); var studentsNoTracking=db.Students.AsNoTracking().ToList(); db.Configuration.AutoDetectChangesEnabled = false; /* *修改对象 * 优化查询 * 需要注意,非空的字段都得写出来 */ Students stu = new Students { StudentAddress = "2222", StudentName = "wan", StudentIdNo = 130223198910111228, ClassId = 2, Gender = "男", Birthday = Convert.ToDateTime("1990-10-11"), Age = 24, PhoneNumber = "88888888", StudentId = 100013 }; db.Entry<Students>(stu).State = System.Data.Entity.EntityState.Modified; db.SaveChanges(); /* *删除对象 */ //将主键值封装到要删除的对象中 Students stu = new Students { StudentId = 100014 }; using (EFDBEntities efdb = new EFdemo.EFDBEntities()) { efdb.Set<Students>().Attach(stu); efdb.Entry(stu).State = System.Data.Entity.EntityState.Deleted; Console.WriteLine(efdb.SaveChanges()); }
-
-
使用本地查换成查询
-
注意事项:如果查询结果没有缓存到本地,是无法获取数据的。例如
-
accounts.FirstOrDefault(); Console.WriteLine("数据库真实的用户数据:{0}", context.Accounts.Count()); Console.WriteLine("只能读取到一条数据,因为FirstOrDefault()只缓存一条:{0}", context.Accounts.Local.Count());
-
using (EFDBEntities efdb = new EFDBEntities()) { var stuList = from stu in efdb.Students select stu; foreach (var item in stuList) { Console.WriteLine(item.StudentId + "\t" + item.StudentName); } //查询学生总数 Console.WriteLine("学生总数:{0}", efdb.Students.Count()); //使用缓存查询学生总数 Console.WriteLine("学生总数:{0}", efdb.Students.Local.Count()); Console.ReadLine(); }
-
-
-
-
EF的事务
-
在查询时没有事务,在增删改时EF会默认开启事务。
-
自定义事务
-
自定义事务可以在某些时候改变事务隔离级别(System.Data.IsolationLevel)7种
-
默认隔离级别:Read Commit
-
实现ReadUncommitted的2种方式(可以进行脏读,意思是说,不发布共享锁,也不接受独占锁。)
/*方式一*/ using (var cusTransaction = db.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted)) { try { db.Database.Log = Console.WriteLine; //我们可以在这个地方添加需要的增删改操作... db.StudentClass.Add(new StudentClass { ClassId = 35, ClassName = ".NET高级学习班5" }); db.StudentClass.Add(new StudentClass { ClassId = 36, ClassName = ".NET高级学习班6" }); db.SaveChanges(); cusTransaction.Commit(); } catch (Exception ex) { cusTransaction.Rollback(); throw ex; } } /*方式二*/ using (var cusTransaction = new TransactionScope()) { try { db.Database.Log = Console.WriteLine; //我们可以在这个地方添加需要的增删改操作... db.StudentClass.Add(new StudentClass { ClassId = 45, ClassName = ".NET高级学习班11" }); db.StudentClass.Add(new StudentClass { ClassId = 46, ClassName = ".NET高级学习班10" }); db.SaveChanges(); cusTransaction.Complete(); } catch (Exception ex) { Transaction.Current.Rollback(); throw ex; } }
-
-
-
EF查询的三种加载模式
-
延迟加载
-
延迟加载是当需要用到数据时,才会进行加载查询,同时会根据主外建生成的导航属性进行查询。
-
var student = db.Students.FirstOrDefault(); /* * 1、首先进行查询,返回一个SqlDataReader对象(Student对象)此时结果已经出来了(直接取除导航属性外的值不会进行二次查询),如果展开,因为这个类有导航属性。 * 2、展开后对2个外键进行查询(班级和成绩) * 3、展开班级外键,在student表查询班级是这个的结果(因为班级关联多个Student对象) * 结论:延迟查询可以对关联的导航属性进行查询。 */
-
关闭延迟加载:关闭延迟加载后不会对导航属性进行查询
- db.Configuration.LazyLoadingEnabled = false;
-
-
显示加载
- 对于已经关闭的延迟加载,我们可以再次手动开启:db.Entry(student).Reference(s => s.StudentClass).Load();
-
立即加载
- 当我们获取一个实体对象的时候,与之关联的其他外键对象会被立即加载。
- var result = db.Students.Include(“StudentClass”).Include(“ScoreList”).ToList();
-
-
EF-DBFirst使用存储过程
-
通过DataAnotation自定义字段
-
FlunetAPI的使用
-
EntityTypeConfiguration:提供了很多配置 实体或属性 的重要方法, 实现覆盖各种Code-First约定
protected override void OnModelCreating(DbModelBuilder modelBuilder) { EntityTypeConfiguration<TEntity> tEntityCfg = modelBuilder.Entity<TEntity>(); base.OnModelCreating(modelBuilder); }
方法名 返回类型 描述 HasKey<TKey>
EntityTypeConfiguration 为此实体配置主键属性 HasMany<TTargetEntity>
ManyNavigationPropertyConfiguration 配置实体类型多对多关系 HasOptional<TTargetEntity>
OptionalNavigationPropertyConfiguration 配置此实体类型的可选关系. 实体类型的实例可以保存到数据库,而不指定此关系. 数据库中的外键将为空. HasRequired<TTargetEntity>
RequiredNavigationPropertyConfiguration 配置此实体类型所需的关系. 实体类型的实例将无法保存到数据库,除非指定了该关系. 数据库中的外键将不可为空. Ignore<TProperty>
Void 从模型中排除属性,使其不会映射到数据库. Map
EntityTypeConfiguration 允许与实体类型映射到数据库模式的高级配置相关. Property<T>
StructuralTypeConfiguration 配置在此类型上定义的struct属性. ToTable
Void 配置此实体映射到数据库的名称. https://docs.microsoft.com/en-us/dotnet/api/system.data.entity.modelconfiguration.entitytypeconfiguration-1?redirectedfrom=MSDN&view=entity-framework-6.2.0
-
-
CodeFirst数据库初始化策略
-
Code First 中支持 4 种数据库初始化策略
-
CreateDatabaseIfNotExists:这是默认的初始化程序。顾名思义,如果没有配置,它将创建数据库。但是,如果更改模型类,然后使用该初始化程序运行应用程序,那么它将抛出异常
-
DropCreateDatabaseIfModelChanges:如果您的模型类(实体类)已更改,则此初始化程序将删除现有数据库并创建新数据库。因此,当您的模型类更改时,您无需担心维护数据库模式
-
DropCreateDatabaseAlway:该初始化程序在每次运行应用程序时都会删除现有数据库,而不管您的模型类是否已更改。当您需要新鲜数据库时,每次运行应用程序时,就像在开发应用程序时一样
-
Custom DB Initializer:您还可以创建自己的自定义初始化程序,如果上述任何一个都不满足您的要求,或者您想使用上述初始化程序初始化数据库的其他进程
Database.SetInitializer<YourDBContext>(new CreateDatabaseIfNotExists<YourDBContext>()); Database.SetInitializer<YourDBContext>(new DropCreateDatabaseIfModelChanges<YourDBContext>()); Database.SetInitializer<YourDBContext>(new DropCreateDatabaseAlways<YourDBContext>()); Database.SetInitializer<YourDBContext>(new YourDBInitializer());
public class YourDBInitializer : CreateDatabaseIfNotExists<YourDBContext> { protected override void Seed(YourDBContext context) { base.Seed(context); } }
-
-