EF6学习笔记十四:上下文管理

要专业系统地学习EF前往《你必须掌握的Entity Framework 6.x与Core 2.0》这本书的做者(汪鹏,Jeffcky)的博客:https://www.cnblogs.com/CreateMyself/

前面学的东西只算入门,为了理解EF,书中的EF进阶非学不可,我看的很吃力。在百度上看关于EF上下文的文章,不多,让人很无助。html

我反复看了几遍,仍是有不少东西不理解,没办法了,那些东西真的没有什么接触,想象不出来。如今只能努力用本身的话复述一遍,并记录一些本身的问题,主要是想记录本身的思考的过程。web

使用EF咱们要本身写一个派生自DbContext的上下文类,这是咱们与数据库交互的桥梁。数据库

我对这个类了解多吗?其实不多。在EF的最第一版本是利用ObjectContext来进行数据访问的,如今的DbContext底层仍是基于ObjectContext,通过改造后的DbContext,性能更好,官方推荐使用DbContext。若是咱们想用ObjectContext也没问题,由于他们之间能够互相转换。缓存

  DbContext 转 ObjectContext安全

using (EFDbContext ctx = new EFDbContext()) { var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)ctx).ObjectContext; } 
View Code

  ObjectContext 转 DbContext多线程

System.Data.Entity.Core.Objects.ObjectContext objCtx = new System.Data.Entity.Core.Objects.ObjectContext("connectionStr"); var ctx = new System.Data.Entity.DbContext(objCtx,true);
View Code

如今来讲我理解的一些上下文管理的东西。并发

上下文生命周期管理

上下文我知道,不过也是后来才知道。当一个东西,不给出上下文你是不能很好的理解他的,由于没有约束,范围太大,对于不一样的环境有不一样的理解。和说话同样,对于一句话你还要给出这句话的语境,别人才能真正知道。异步

当我说出一句“真香”,你知道我说的是真的香仍是真香定律?这和泛型约束同样,咱们对类型添加的那些约束也能够看作是这个类型的上下文。async

我最开始弄EF,一样的是派生一个上下文,可是那时我给上下文取的变量名是“db”,由于我就认为它是一个虚拟的数据库。可是以为不许确,并且直到我无心中看到上下文中有个属性叫Database……分布式

 

若是上下文就是数据库,那么它怎么还要有一个Database属性呢?确定不是这样的,Database应该看作是EF这个语境中的一句话,这个大语境中还有配置、追踪……这些内容,因此要理解整个语境才能理解一句话。弄懂上下文和他里面database的关系。

生命周期,什么意思,按照字面意思来讲,一我的从出生到死亡的过程就是这我的的生命周期,一个手机生产到使用报废的过程,再怎么玄之又玄的解释确定不能怀疑不能动摇,它就是一个出生到死亡的过程。

我这说了好像等于没说啊,其实,我想要对本身强调一遍。根本性的东西不能动摇。既然它叫了这个名字,那么若是实际状况不是这样,只能说明是它的问题不是个人问题。

由于概念性的东西总是听别人说来讲去,一个这样说,另外一个那样说,若是你不去继续研究,他对你就是一个模糊的、说不出来的东西,并且会怀疑他的命名。

上下文生命周期管理,说的就是……上下文生命周期管理

上下文的生命周期开始于初始化,结束于被垃圾回收器回收并释放。释放?你释放就是了,你不是内存托管吗?

原来,要是你不使用using来包括上下文,就要本身手动释放资源。

但是你不是内存托管吗?我本身建立一个Studen类,也历来不用去手动释放啊

在之前写ado的时候我记得要建立数据库链接,用try/catch包裹起来,最后在finally中调用close方法关闭链接。

后来我知道了托管代码和非托管代码这一个东西。个人理解就是只要你不写指针不本身操做内存,那么就是写的托管代码。

因此我认为是否是有些东西不能托管,百度了一下果真:

系统的资源包括托管资源和非托管资源。例如文件流,数据库的链接,系统的窗口句柄,打印机资源等等这些是非托管资源。” https://www.cnblogs.com/enamorbreeze/p/4711468.html

那就必定是上下文中使用了非托管资源了,难道是上下文对象不销毁,那么由它建立的数据库链接就不会关闭?应该不是,由于我在前面弄EF时候看过EF生成的SQL和SQL的执行状况

好比我根据导航属性订单来查询产品,订单和产品一对多的关系

var res = ctx.Products.Where(x => x.Order != null).ToList(); Console.WriteLine(JsonConvert.SerializeObject(res, set));

SQL的执行状况是这样

能够看到EF每执行一次SQL,就会打开/关闭一次数据库链接

那就说明他本身有关闭数据库链接的能力啊,并非个人上下文对象销毁了,数据库链接才关闭。

可是总的来讲上下文你不使用using来包裹,就须要手动释放资源,确定是用到了哪些非托管资源。

如何更干净地使用上下文

 我一开始使用EF,实例化上下文和普通对象是同样的写法,由于我不知道除非using包裹就要手动释放资源。

using几年前我就见到了,一直没有去弄过,不知道怎么回事,在写ado的时候我用的try/catch也看到有人写using

原来这个using能够看作就是try/catch的语法糖,using通过反编译后的本质是建立try/finally块,并最终在finally中调用Dispose方法释放。

书中这样说:"在使用上下文时,始终要谨记一个准则:一个请求对应一个上下文。在项目中上下文全局惟一,这种方式显然不可取,在多线程web应用程序中性能极低,尤为对并发操做。"

咱们来看三种上下文使用方式

Using Pattern  使用using,这种方式的缺点,控制器中的方法可能用到上下文的状况很是多,这样就会处处充斥这些using

using(EFDbContext ctx = new EFDbContext()) { }
View Code

DisPose Pattern   上下文和控制器一同建立和销毁

public class TestController : Controller { private EFDbContext _ctx; public TestController() { _ctx = new EFDbContext(); } protected override void Dispose(bool disposing) { if (disposing) { _ctx.Dispose(); } base.Dispose(disposing); } }
View Code

 Dependency Injection  依赖注入的方式是最被开发者推荐的模式

public class TestController : Controller { private EFDbContext _ctx; public TestController(EFDbContext ctx) { _ctx = ctx; } }
View Code

 如今上下文就不是由控制器来建立和销毁了,并且被注入进来的,控制器只管使用就好了。我对依赖注入没有什么认识,一直没有去弄。注入明白,依赖呢?难道是这个被注入的对象和控制器的构造函数绑定在一块儿了,两者缺一不可,造成了依赖关系?

行吧,书中讲到了生命周期追溯,这里我就实在看不明白了,由于缺乏经验,彻底想象不出那种多数据库,分布式系统多线程和并行操做那种对我来讲很陌生的环境,因此,这里我就不说了。

做者说上下文派生实例并不是类线程安全,所以咱们不能再多线程中访问上下文实例,这会形成屡次查询经过相同的数据库链接同时并发返回,也会破坏经过上下文维护实体的变动追踪和工做单元的一级缓存,因此在多线程应用程序中必须为每一个线程使用独立的上下文派生实例。

所以EF6.x引入了异步查询功能,异步查询只支持在同一时刻查询一次,超过一次就会抛出异常

来看一下

var res1 = ctx.Products.ToListAsync(); var res2 = ctx.Products.ToListAsync();
View Code 

这样直接报错,而且VS崩溃

须要使用await来等待上一次查询完成,再接着执行下一步的查询

public async Task<string> Test() { JsonSerializerSettings set = new JsonSerializerSettings(); set.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; string result = string.Empty; using (EFDbContext ctx = new EFDbContext()) { var res1 = await ctx.Products.ToListAsync(); var res2 = await ctx.Products.ToListAsync(); var res3 = res1.Concat(res2); result = JsonConvert.SerializeObject(res3,set); } return result; } Task<string> res = new Program2().Test(); Console.WriteLine(res.Result);
View Code

 

 行了,就这吧。弄明白了一些,也有不少没弄明白,路漫修远兮。

 

原文出处:https://www.cnblogs.com/jinshan-go/p/10293396.html

相关文章
相关标签/搜索