MVC 2.0+Entity Framework在使用依賴注入時遇到的問題
前言:
最近在看MVC 3.0 Beta Release Note的時候,發現了IDependencyResolver接口,又看到了兩篇關于MVC下使用依賴注入的文章(傳送門1,傳送門2)。
于是乎,對依賴注入進行了學習,并對以前做的一個網站進行了重構(使用Unity)。
其實完全沒有必要,因為無法充分體現依賴注入的優勢,反而在性能上有點損失,就當練手一下吧~
在這個過程中(網站是MVC2.0做的),發現MVC2.0和Entity Framework在依賴注入過程中出現了一些問題,所以在此,詳解一下。
MVC 2.0下實現依賴注入:
MVC2.0對依賴注入的支持度很小,理解依賴注入的原理后大家都知道,這里的關鍵就是需要找到一個切入點,分析對象之間的關系和生命周期后發現,從Controller切入是一個明智的選擇。
那怎么攔截原本的Controller創建過程,替代成我們自己的創建過程呢?
看了RoRoWoBlog的源碼和傳送門1這篇文章后,發現MVC中的Controller是由 DefaultControllerFactory 這個類創建的,很明顯,這是一個Controller工廠類,職責就是用來創建Controller對象的。
反編譯之:
可以發現內部有大量的虛方法,而且從名字中可以看出,這是這個工廠的關鍵,繼承并重寫之即可。
public class MyConrtollerFctory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (requestContext == null || controllerType == null)
{
throw new ArgumentNullException("controller");
}
return MvcApplication.Container.Resolve(controllerType) as IController;
}
}
接下來利用一個靜態方法在Global.asax中替換默認的Controller工廠
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//替換默認Controller工廠
ControllerBuilder.Current.SetControllerFactory(new MyConrtollerFctory());
}
如何正常地拋出異常:
一切看似是那么地順利,運行一下,成功了!
但是,忽然發現問題了!
當初輸入一個不存在的Controller的時候,竟然報錯了!而不是返回404錯誤!
高亮的那段話,顯示問題出在這,看上去是在替代這個方法的時候,沒有正確地拋出異常
繼續反編譯,查看這個方法:
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(0x194, string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, new object[] { requestContext.HttpContext.Request.Path }));
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, new object[] { controllerType }), "controllerType");
}
return this._activatorResolver.Current.Create(requestContext, controllerType);
}
看上去問題就出在這了,由于我們拋出了自定的異常,導致外層沒有Catch到,所以它認為這不是404錯誤
既然知道了原因,那解決起來豈不是很簡單?直接把這段代碼拷貝過來就行了~
但是問題又來了,這段代碼中有幾個對象是 internal(程序集可見)
我們的代碼和它不在同一個程序集,那當然也不能訪問了… 怎么辦?繼續反編譯?把代碼復制過來?
這個方案恐怕很不友好,其實,有一個小技巧,雖然看起來不怎么樣,但是它的確有效~
public class MyConrtollerFctory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (requestContext == null || controllerType == null)
{
//關鍵
base.GetControllerInstance(requestContext, controllerType);
}
return MvcApplication.Container.Resolve(controllerType) as IController;
}
}
將重寫Controller工廠類的時候,如果需要拋出異常,就直接訪問基類的方法,讓基類去拋吧~ 這樣就不用擔心什么了~
Entity Framework中遇到的問題:
不僅是MVC2.0,EF對依賴注入的支持度也不高。
主要體現在,ObjectContext 是由工具自動生成的,可以修改,但不建議修改它。
而 ObjectContext 又是有多個構造函數的,要么寫配置文件,要么給它的構造函數上加上 InjectionConstructor 這個Attribute。
前者太麻煩,我只是一個小項目,沒必要寫配置文件;后者要修改核心代碼,不符合上面的原則。
其實還有一種很方便的辦法可以解決這個問題,只要在 UnityContainer 中注冊一下 ObjectContext 的構造函數即可。
/// <summary>
/// Unity容器
/// </summary>
static private UnityContainer container;
static public UnityContainer Container
{
get
{
if (container == null)
{
container = new UnityContainer();
//注冊構造函數
container.RegisterInstance<ModelContainer>(new ModelContainer());
}
return container;
}
}
后記:
據VC 3.0 Beta對依賴注入有了很好的支持(傳送門2),期待~ 希望在正式版中能確定一下用法,不要一直變了~

浙公網安備 33010602011771號