上篇中"Entity Framework中的Identity map和Unit of Work模式", 由於EF中的Identity map和Unit of Work模式,EF體現出來如下特性:
唯一性: 在一個Context的生命週期中,一個Entity只會有一個實例,任何對該實例的修改,即使這些改動沒有保存到數據庫中,修改都會影響到整個Context的生命週期。
事務性: 所有對於Entity的修改,都會在調用SaveChange方法的時候,一起保存到數據庫中,最終實現持久化。
下面基於EF的上面特點,分析一下爲什麼需要在MVC中實現One Context Per Request, 也就是在一個Request生命週期中,只有一個Context.
閱讀目錄:
一、每次創建Context的缺點
二、使用全局Context的缺點
三、在MVC中實現One Context Per Request
四、藉助Autofac實現One Context Per Request
一,每次創建Context的缺點
一般在項目的數據訪問層中使用Entity Framework,代碼如下
public IEnumerable<Student> GetStudents() { using (var context = new SchoolContext()) { return context.Students.ToList(); } }
這個是數據訪問層中非常常見的方法,返回DB中所有的Student數據。
這裏在使用Context的時候,創建一個Context的實例進行操作。
但是這種方式帶來了下面一些缺點:
- 首先,每次的數據處理,都用new context, 會導致更多的資源開銷。
- 假如業務邏輯層調用GetStudents方法獲取到數據之後,要訪問Student的導航屬性School怎麼辦? 邏輯層代碼使用導航時候就會導致異常,因爲EF只能在context生命週期中,才能夠再次請求數據庫,取得導航屬性School的數據。
- 如果是插入操作,而且是多個關聯表的數據插入,插入操作在不同的context中完成,就無法應用EF的事務效果。保證數據能夠同時插入成功,如果失敗,就一起回滾。
- 如果在循環中插入數據,每次插入數據都是在不同的context中完成,性能就是一個悲劇。
二,使用全局Context的缺點
看到了"每次創建Context”的缺點,可能會認爲使用全局Context是個好的解決方案。
但是全局Context帶來的問題更大:
- 如果全局使用一個Context,會導致越來越多的數據緩存到本地, 隨着程序的使用時間越長,佔用的資源越來越大。
- 使用全局Context, 會導致緩存數據無法得到及時更新。即使數據庫中的數據有改動,使用EF取出來得數據有可能還是改動之前的數據。
所以:
- 在MVC項目中,建議每個request, 使用一個Context
- 在Winform中和WPF中,一個Form或者一個Presenter一個Context
- 在WebService, Web API中,每次調用, 使用一個Context.
三, 在MVC中實現One Context Per Request
思路是這樣的, 在Global.asax.cs文件中,在Begin Request事件中,創建和保存Context; 在End Request事件中,銷燬Context. 另外提供一個公開的靜態屬性來獲取這個Context。
詳細的代碼如下:
在Global.asax.cs中
protected virtual void Application_BeginRequest() { HttpContext.Current.Items["_EntityContext"] = new EntityContext(); } protected virtual void Application_EndRequest() { var entityContext = HttpContext.Current.Items["_EntityContext"] as EntityContext; if (entityContext != null) entityContext.Dispose(); }
添加靜態屬性,以便程序中能夠方便的取出和使用Context
public class EntityContext { public static EntityContext Current { get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; } } }
四,藉助Autofac實現One Context Per Request
Autofac是.net的Ioc容器,具體使用的方法,可以看這裏 IoC容器Autofac(4) - Autofact + Asp.net MVC + EF Code First(附源碼)
本文的Demo源碼,是在上面博客附帶的源碼基礎上修改而來的。
這裏,只是介紹一下如何使用Autofac註冊Context
在Application_Start函數體內,執行如下代碼
var builder = new ContainerBuilder(); //創建builder //註冊builder, 實現one context per request builder.RegisterType<eassistdevContext>().InstancePerHttpRequest(); var container = builder.Build();//創建容器 DependencyResolver.SetResolver(new AutofacDependencyResolver(container));//覆蓋MVC默認的實例化Controller的方法,轉而又Auotfac容器提供