EntityFramework中的DbContext使用疑点说明

时间:2022-12-06 23:44:08

1.DbContext怎么在Asp.mvc中使用?

  public class Repository
    {
        //实例化EF容器:有弊端。一个线程里可能会创建多个DbContext
        //DbContext db = new DbContext();

        //改造:保证一个请求线程中只有一份EF容器(你要明白:一个url请求到服务器,IIS就开一个线程去处理)
        protected DbContext GetDbContext
        {
            get
            {
                //向线程缓存中查询,如果返回的是null,则创建,同时存入到这个线程缓存中
                //注意的是线程缓存CallContext,而不是我们熟悉的HttpRuntime.cache。意味着这个DbContext对象在这个线程内能被其他方法共享。
                object efDbContext = CallContext.GetData("DbContext");
                if (efDbContext == null)
                {
                    efDbContext = new DbContext();
                    //存入到这个线程缓存中
                    CallContext.SetData("DbContext", efDbContext);
                }
                return efDbContext as DbContext;
            }
        }
  }

  这么定义之后,所有需要用到DbContext对象的地方,都调这个方法。

2. 不要随便using或Dispose DbContext会导致延迟加载的不可用,还会有一些其他错误 如IQueryable<T> 下面的方法(.First() /.Count())也不能用。

情况一:

  a写法结果正确

PhoneBookEntities phoneBookEntities = new PhoneBookEntities();
var ci = phoneBookEntities.ContactInfo.FirstOrDefault();//这里并没有拿到ci对象里面的GroupInfo属性。
if (ci != null) MessageBox.Show(ci.GroupInfo.GroupName);//是延迟加载,访问 ci.GroupInfo.GroupName 才会去数据库查数据

  b写法报错

ContactInfo ci = null;
using (PhoneBookEntities phoneBookEntities = new PhoneBookEntities())
{
	ci = phoneBookEntities.ContactInfo.FirstOrDefault();
}

if (ci != null) MessageBox.Show(ci.GroupInfo.GroupName);
//报错:此ObjectContext实例已释放,不可再用于需要连接的操作。意味着延迟加载不可用。

情况二:

  a写法报错

IQueryable<ContactInfo> ci = null;
using (PhoneBookEntities phoneBookEntities = new PhoneBookEntities())
{
	ci = phoneBookEntities.ContactInfo.Where(c => true);
}

if (ci != null) MessageBox.Show(ci.Count().ToString());//报错:提示DbContext已经释放。

  b写法正确

IQueryable<ContactInfo> ci = null;
using (PhoneBookEntities phoneBookEntities = new PhoneBookEntities())
{
	ci = phoneBookEntities.ContactInfo.Where(c => true);
	if (ci != null) MessageBox.Show(ci.Count().ToString());//可以返回正确结果。
}

  

3.为什么你要using 或dispose掉DbContext ?

是担心数据库连接没有释放?还是担心DbContext占用过多资源呢?
首先担心数据库连接没有释放肯定是多余的,因为DbContext在SaveChanges完成后会释放掉打开的数据库连接。
可以反编译一下SaveChages的源码。
担心DbContext占用过多资源也是多余的,有GC回收。

结论,You can call Dispose, but in most common scenarios you don’t need to.

更详细的可以看这个英文博客的文章,其中有 Diego Vega (the Senior SDE Lead on EF) 的回信

Hello Jon,

The default behavior of DbContext is that the underlying connection is automatically opened any time is needed and closed when it is no longer needed. E.g. when you execute a query and iterate over query results using “foreach”, the call to IEnumerable<T>.GetEnumerator() will cause the connection to be opened, and when later there are no more results available, “foreach” will take care of calling Dispose on the enumerator, which will close the connection. In a similar way, a call to DbContext.SaveChanges() will open the connection before sending changes to the database and will close it before returning.

Given this default behavior, in many real-world cases it is harmless to leave the context without disposing it and just rely on garbage collection.

That said, there are two main reason our sample code tends to always use “using” or dispose the context in some other way:

1. The default automatic open/close behavior is relatively easy to override: you can assume control of when the connection is opened and closed by manually opening the connection. Once you start doing this in some part of your code, then forgetting to dipose the context becomes harmful, because you might be leaking open connections.

2. DbContext implements IDiposable following the recommended pattern, which includes exposing a virtual protected Dispose method that derived types can override if for example the need to aggregate other unmanaged resources into the lifetime of the context.

 

By the way, with DbContext the pattern to open the connection manually and override the automatic open/close behavior is a bit awkward:

((IObjectContextAdapter)dbContext).ObjectContext.Connection.Open()

But we have a bug to make this easier as it used to be with ObjectContext before, e.g.:

dbContext.Database.Connection.Open()

Hope this helps,

Diego

附上参考博客:http://www.cnblogs.com/mecity/archive/2011/07/17/2108508.html