本人在EF3.5版本刚发布的时候那时候就使用在项目中,好多年前了,那时候就考虑到EF上下文怎么创建的问题,以下是浅谈
在一次请求中,即当前的一个请求线程中,刚开始的时候在使用EF的时候每次都new一个,当然为了自动释放对象我们会用using包起来(数据库连接是CLR非托管资源,系统不会自动释放), 但是如果有一个业务逻辑调用了多个dal层的方法,交互数据库多次,这样效率会低一些,而且在使用EF的情况下,我们通常把SaveChange这个方法提到业务逻辑层,不保证同一个业务逻辑使用的是同一个上下文对象,事务,工作单元模式(DDD,仓储以后会发相关文章)将无法实现。而且可能造成数据混乱,每次创建的对象执行相应的数据库操作,与此同时,同一次的请求可能包含对数据的不同操作。其他的EF对象内获得的数据可能已经是“过期”的了。即数据可能变动过。
关键就是上下文对象的创建问题。这里最先当然想到Singleton,不过在这里,不适合用,原因是使用单例模式,会使EF对象得不到及时的资源释放。想象一下,无数个请求对数据库的访问,DbContext对象容器无数次增加对Model对象的变更监控,内存就爆了。而且因为EF对象不是简单的对象包含数据库实体以及Iqueryable等等。
最先开始使用的数据巢也就是CallContext保证在线程内唯一,每一个请求使用同一个上下文 CallContext具体说明请翻阅MSDN 接下来废话不多说 上代码
/// <summary>
///SW 用来创建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 SwEntities();
CallContext.SetData("dbContext", dbContext);
}
return dbContext;
}
}
如果想保证一些并发 请使用行锁Lock 这里不是重点不细说
这样用了一段时间,思考了一下发现有一些问题,上面使用CallContext来存储有什么问题?就是说上面是把上下文对象依赖于一个线程。那么由于线程池的存在,线程在处理完一个请求之后,并没有被销毁,存储在CallContext中的上下文对象也一直存在,如果是下一次拿出这个线程去处理另一个请求,这个上下文对象其实也在不断的膨胀,只不过比全局的膨胀的稍微慢一些。而且,有时候一个线程并不一定是拿去处理请求了,如果是服务器拿去处理其他的业务,那就可能引发一些其他的问题,不能解决本质问题。
通过查阅资料研究发现 其实我们还有更好的办法,在HttpContext中有一个Items属性,它也可以用来保存key-value,这就完美了,一次请求正好对应着一个HttpContext,请求结束,它自动释放,EF上下文也就不存在了。把上面代码中的CallContext改为HttpContext.Current.Items,废话不多说,上代码:
public static DbContext DbContext()
{
DbContext dbContext = HttpContext.Current.Items["dbContext"] as DbContext;
if (dbContext == null)
{
dbContext = new SwEntities();
HttpContext.Current.Items["dbContext"] = dbContext;
}
return dbContext;
}