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對象的地方,都調這個方法。
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());//可以返回正確結果。 }
是擔心數據庫連接沒有釋放?還是擔心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