EF上下文对象线程内唯一性与优化
在一次请求中,即一个线程内,若是用到EF数据上下文对象,就创建一个,这也加是很多人的代码中习惯在使用上下文对象时,习惯将对象建立在using中,也是为了尽早释放上下文对象, 但是如果有一个业务逻辑调用了多个dal层的方法,交互数据库多次,这样效率会低一些,而且在使用EF的情况下,我们通常把SaveChange这个方法提到业务逻辑层(下文中会提到),不保证同一个业务逻辑使用的是同一个上下文对象,事务,工作单元模式将无法实现。而且可能造成数据混乱,每次创建的对象执行相应的数据库操作,与此同时,同一次的请求可能包含对数据的不同操作。其他的EF对象内获得的数据可能已经是“过期”的了。即这个数据已经变动过。这就是脏读。
为了解决这个问题,关键就是上下文对象的创建问题。
这里首先想到单例模式,不过在这里,不适合用,原因是使用单例模式,会使EF对象得不到及时的资源释放。想象一下,无数个请求对数据库的访问,DbContext对象容器无数次增加对Model对象的Attach监控,内存就爆了。
优化就是折中的过程,所以第二种方式考虑保证在线程内对象唯一,对于每一个请求使用同一个上下文。如何保证呢,通过微软ASP机制线程相关的HttpContext对象以及CallContext对象。前面一篇文章中说过,HttpContext机制其实就是依靠CallContext对象实现的。先来看使用CallContext解决这个问题
你可以这样做,在网站Common中添加处理类:
- /// <summary>
- /// 用来创建EF上下文对象,且保证线程内唯一。
- /// </summary>
- public class DbContextFactory
- {
- //DbContext在System.Data.Entity;中,不过这里直接只引用这一个不行,还有EF其他的一些NameSpace所以直接添加一个实体模型,所有引用都进来了,然后再把模型删了
- public static DbContext GetDbContext()
- {
- DbContext dbContext = (DbContext)CallContext.GetData("dbContext");
- if (dbContext == null)
- {
- dbContext = new WebEntities();
- CallContext.SetData("dbContext", dbContext);
- }
- return dbContext;
- }
- }
是不是很像缓存的使用策略。
仔细思考一阵后发现,上面使用CallContext来存储有什么问题?就是说上面是把上下文对象依赖于一个线程。那么由于线程池的存在,线程在处理完一个请求之后,并没有被销毁,存储在CallContext中的上下文对象也一直存在,如果是下一次拿出这个线程去处理另一个请求,这个上下文对象其实也在不断的膨胀,只不过比全局的膨胀的稍微慢一些。而且,有时候一个线程并不一定是拿去处理请求了,如果是服务器拿去处理其他的业务,那就可能引发一些其他的问题。
所以,改进一下上面的办法,借鉴一下J2EE的hibernate和mybatis,在DbContextFactory中添加一个remove方法,在业务逻辑层中每次请求使用完上下文之后,就把它从线程中移除。
解决了,可是这办法实在是。。。那如果我一次请求要调几次业务逻辑呢,还是要创建多次上下文。而且这样手动管理的方式,让人痛苦。相信也是由于这个原因,在spring+hibernate中大家也是更愿意用HibernateTemplate而不是HibernateDaoSupport。
其实我们还有更好的办法,在HttpContext中有一个Items属性,它也可以用来保存key-value,这就完美了,一次请求正好对应着一个HttpContext,请求结束,它自动释放,EF上下文也就不存在了。把上面代码中的CallContext改为HttpContext.Current.Items,OK。
- public static DbContext DbContext()
- {
- DbContext dbContext = HttpContext.Current.Items["dbContext"] as DbContext;
- if (dbContext == null)
- {
- dbContext = new WebEntities();
- HttpContext.Current.Items["dbContext"] = dbContext;
- }
- return dbContext;
- }
再说SavaChanges这个方法,我们现在可以做到EF上下文创建的优化,那么它对数据库的交互呢?这是我们写了无数次的方法:
- public int AddUser(User user)
- {
- context.Add(user);
- return context.SaveChanges();
- }
当我们使用一个业务逻辑复杂的方法中,它可能需要使用到多个dal层对象或者说调用多次dal层的方法,上面的写法,调几次,EF就与数据库交互了几次,效率还是很低。那我们何不把与数据库的交互方法SaveChanges()提到bll层来调用,由bll层方法来调用,一次的业务逻辑,只交互一次,形成一种工作单元模式。
那么怎么提取,由于我们上下文对象在请求内唯一,那么就再简单不过了。
- public class DbSession
- {
- public static int SaveChanges()
- {
- return DbContextFactory.GetDbContext().SaveChanges();
- }
- }
为什么把这个类名取为DbSession,学习JavaEE的朋友可能马上想到了MyBatis,Hibernate,我们封装了一个对数据库的单元操作,与数据库进行交互,就是一次与数据库的会话。
另外,我刚接触EF的时候就有这个疑问,EF如果做到事务的处理,用TransactionScope或DbConnection?大可不必,如果我们把SaveChanges()提到业务逻辑层,就组成了一个事务单元,再联想一下spring,为什么会把声明式事务放在Service层而不是Dao层,而且SaveChanges()这个方法其实本身事务的特性,如果保持了上下文对象的唯一性,间接也是完成了事务单元。
===========================2016-11-7===========================
最近在MVC里面用了一下NHibernate,仍然需要像管理EF上下文一样管理Session对象,同样,我们也可以把它"缓存"在HttpContext中,但是NHibernate已经帮我们完成了类似的工作。详情参见http://www.cnblogs.com/13yan/archive/2013/05/17/3083552.html。作者还给大家提供了一个NHibernateHelper,很赞。
EF上下文对象线程内唯一性与优化的更多相关文章
- EF上下文对象创建之线程内唯一
在一次请求中,即一个线程内,若是用到EF数据上下文对象,就创建一个,那么会造成数据混乱,每次创建的对象执行相应的数据库操作,此同时,其他的EF对象内获得的数据可能已经是“过期”的了.即这个数据已经变动 ...
- 如何保证对象线程内唯一:数据槽(CallContext)
CallContext 是类似于方法调用的线程本地存储区的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽.数据槽不在其他逻辑线程上的调用上下文之间共享.当 CallContext 沿执行代码路径 ...
- 【无私分享:ASP.NET CORE 项目实战(第二章)】添加EF上下文对象,添加接口、实现类以及无处不在的依赖注入(DI)
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 上一章,我们介绍了安装和新建控制器.视图,这一章我们来创建个数据模型,并且添加接口和实现类. 添加EF上下文对象 按照我们以前 ...
- ASP.net如何保证EF操作类线程内唯一
说到线程内唯一,肯定会想到单例模式,但是如果多用户访问网站就会出现问题.ASP.net中有两种方法可以保证EF操作类线程内唯一(目前只会这两种,以后有好的方法再添加): 1.httpcontext(实 ...
- 添加EF上下文对象,添加接口、实现类以及无处不在的依赖注入(DI)
添加EF上下文对象,添加接口.实现类以及无处不在的依赖注入(DI) 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 上一章,我们介绍了安装和新建控制器.视图,这一章我们来创建 ...
- C# 如何保证对象线程内唯一:数据槽(CallContext)
如果说,一个对象保证全局唯一,大家肯定会想到一个经典的设计模式:单例模式,如果要使用的对象必须是线程内唯一的呢? 数据槽:CallContext,ok看下msdn对callcontent的解释. Ca ...
- C# 如何保证对象线程内唯一:数据槽(CallContext)【转载】
如果说,一个对象保证全局唯一,大家肯定会想到一个经典的设计模式:单例模式,如果要使用的对象必须是线程内唯一的呢? 数据槽:CallContext,ok看下msdn对callcontent的解释. Ca ...
- MVC+Ef项目(3) 抽象数据库访问层的统一入口;EF上下文线程内唯一
抽象一个数据库访问层的统一入口(类似于EF的上下文,拿到上下文,就可以拿到所有的表).实际这个入口就是一个类,类里面有所有的仓储对应的属性.这样,只要拿到这个类的实例,就可以点出所有的仓储,我们在 R ...
- EF 保证线程内唯一 上下文的创建
1.ef添加完这个对象,就会自动返回这个对象数据库的内容,比如下面这个表是自增ID 最后打印出来的ID 就是自增的结果 2.lambda 中怎么select * var userInfoList = ...
随机推荐
- Angular企业级开发(5)-项目框架搭建
1.AngularJS Seed项目目录结构 AngularJS官方网站提供了一个angular-phonecat项目,另外一个就是Angular-Seed项目.所以大多数团队会基于Angular-S ...
- ArcGIS 10.0紧凑型切片读写方法
首先介绍一下ArcGIS10.0的缓存机制: 切片方案 切片方案包括缓存的比例级别.切片尺寸和切片原点.这些属性定义缓存边界的存在位置,在某些客户端中叠加缓存时匹配这些属性十分重要.图像格式和抗锯齿等 ...
- 如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念
一.前言 DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了.自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平 ...
- Pivot 和 Unpivot
在TSQL中,使用Pivot和Unpivot运算符将一个关系表转换成另外一个关系表,两个命令实现的操作是“相反”的,但是,pivot之后,不能通过unpivot将数据还原.这两个运算符的操作数比较复杂 ...
- C# 条形码操作【源码下载】
本篇介绍通过C#生成和读取一维码.二维码的操作. 目录 1. 介绍:介绍条形码.条形码的分类以及ZXing.Net类库. 2. 一维码操作:包含对一维码的生成.读取操作. 3. 二维码操作:包含对二维 ...
- Ngrok让你的本地Web应用暴露在公网上
1.Ngrok介绍 Ngrok是一个反向代理,通过在公共的端点和本地运行的Web服务器之间建立一个安全的通道.Ngrok可捕获和分析所有通道上的流量,便于后期分析和重放.简单来说,利用 Ngrok可以 ...
- 这些.NET开源项目你知道吗?.NET平台开源文档与报表处理组件集合(三)
在前2篇文章这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧 和这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,大伙热情高涨.再次拿出自己的私货,在.NET平台 ...
- Python应用03 使用PyQT制作视频播放器
作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁任何形式转载. 最近研究了Python的两个GUI包,Tkinter和PyQT.这两个GUI包的底层分别是Tcl/ ...
- js:给定两个数组,如何判断他们的相对应下标的元素类型是一样的
题目: 给Array对象原型上添加一个sameStructureAs方法,该方法接收一个任意类型的参数,要求返回当前数组与传入参数数组(假定是)相对应下标的元素类型是否一致. 假设已经写好了Array ...
- 写出易调试的SQL
h4 { background: #698B22 !important; color: #FFFFFF; font-family: "微软雅黑", "宋体", ...