前言
- 在编写代码的时候,我遇到了很多关于EntityFramework6的疑问,所以现在就提前把这些问题列出来做一下解答,以便在以后的代码编写过程中减少不必要的Bug。
EntityFramework6的延迟加载(Lazy Loading)是怎么一回事?
- 首先EntityFramework6的延迟加载默认是被支持的,可以通过设置context.Configuration.LazyLoadingEnabled = false来禁止。
- 当启用延迟加载的时候,EntityFramework访问实体的相关对象时,也就是一对一、一对多的属性时,EntityFramework会从你定义的实体派生出一个动态对象,然后覆盖你的子实体集合访问属性来实现。所以我们在定义POCO类的时候,会在关系属性前加上virtual 来修饰。如果不加修饰,就不会启动延迟加载。
- 延迟加载,就是在查找某个实体时,会把相关联的实体对象或者集合全部加载出来。
- 启用延迟加载的情况,执行以下语句
Sql Server Profiler监视情况如下:
从上面的结果可以看出,当我们需要获取Role对象的时候,EntityFramework会把与之关联的对象以动态代理对象(DynamicProxy)
的方式加载出来,数据库也向与之关联的表执行了多个关联查询才得到结果。
2. 禁用延迟加载的情况,执行以下语句
Sql Server Profiler监视情况如下:
从上面的结果可以看出,执行的结果只有Role对象,与之关联的对象S_Menus为null,S_Users也没有结果,说明在禁用延迟加载的情况下,
EntityFramework只对当前执行的上下文对象进行查询。Sql Server Profiler监视情况的结果也是如此,程序只执行了对S_Role表的读取
EntityFramework6的DbContext 适合单例模式吗?
- 之前我们在用三层架构的时候,我们通常会把DBHelper类的支持方法设置为静态方法,这样我们不需要实例化就可以调用执行方法。因此我在想DbContext我们能不能使用单例模式来进行创建?答案是不可以的。因为在同一个DbContext实例上下文中,如果A正在编辑某条数据,B也正在编辑某条数据,B在编辑后进行SaveChanges操作,就会把A编辑的数据也进行提交。而实际A编辑的数据可能被取消,但是也提交到了数据库进行更改,这肯定不是我们需要的结果。
- 如果我们创建了多个DbContext的实例,就可能会遇到并发的问题,因为不同的实例可能会对同一条数据进行修改。EntityFramework可以使用乐观锁来解决此问题。
EntityFramework6怎样使用事务?
- EntityFramework的DbContext中的SaveChanges就自带事务与分布式事务。
- 如果我们的语句是在同一个DbContext的实例进行提交,那么DbContext中的SaveChanges就会默认启动事务,如果有一条语句执行失败,那么事务就会回滚。SaveChanges不会执行。
- 如果我们的语句是在不同的DbContext的实例中进行提交,那我们就要采用分布式事务,也就是TransactionScope来开启。在TransactionScope中的任意一个DbContext的SaveChanges行失败后,其他的实例的SaveChanges也不会被执行,从而达到了事务控制的目的。
EntityFramework6中有缓存实现吗?
- 就目前而言,EntityFramework的缓存只是维护其内部的DbContext,也就是常说的一级缓存。如果要对EntityFramework的查询结果对象进行缓存,我们就需要使用二级缓存来进行。常用的一些分布式应用缓存,如memcached或Redis都可以实现。CodePlex上也提供了一些二级缓存的实例,我们给以通过Nuget直接下载使用,比如下面的这俩个缓存组件:
EntityFramework.Cache https://efcache.codeplex.com/
IQueryable, IEnumerable, IList 傻傻分不清楚?
以下结论引用园友Teddy Li的博客:
- IQueryable和IEnumerable都是延时执行(Deferred Execution)的,而IList是即时执行(Eager Execution)
- IQueryable和IEnumerable在每次执行时都必须连接数据库读取,而IList读取一次后,以后各次都不需连接数据库。前两者很容易造成重复读取,性能低下,并且可能引发数据不一致性
- IQueryable和IEnumerable的区别:IEnumberalb使用的是LINQ to Object方式,它会将AsEnumerable()时对应的所有记录都先加载到内存,然后在此基础上再执行后来的Query。
- 基于性能和数据一致性这两点,我们使用IQueryable时必须谨慎,而在大多数情况下我们应使用IList。当你打算马上使用查询后的结果(比如循环作逻辑处理或者填充到一个table/grid中),并且你不介意该查询会即时执行,使用ToList();当你希望查询后的结果可以供调用者(Consummer)作后续查询(比如这是一个"GetAll"的方法),或者你希望该查询延时执行,使用AsQueryable()