最近留意了一下ASP.NET MVC 的依賴注入,也看了不少相關(guān)的文章,自己也嘗試了兩種,分別為 NInject 和 Unity ,
在使用的過程中,也漸漸的了解了依賴注入的思想,于是從網(wǎng)上下載了一些相關(guān)的代碼,直接拿來用之,包括來自微軟官方的,
也有來自國外牛人博客的,但是使用當中也發(fā)生了一些問題,主要問題就是,當客戶端請求一個不存在的Controller或者Action的時候
(甚至是請求一個不存在的圖片或者資源),會產(chǎn)生異常,網(wǎng)上的大部分代碼都會產(chǎn)生錯誤,這跟使用什么樣的DI框架沒有關(guān)系,
原因就出在覆蓋 DefaultControllerFactory 的 GetControllerInstance 方法的實現(xiàn)上,當遇到不存的Controller 或者 Action 的時候,
拋出的是自定義的異常,而不是 HTTP 異常,于是打開了 MVC 的源碼,仔細閱讀了 DefaultControllerFactory.GetControllerInstance 的實現(xiàn),
發(fā)現(xiàn)如下代碼片斷:
if (controllerType == null) {
throw new HttpException(404,
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_NoControllerFound,
requestContext.HttpContext.Request.Path));
}
if (!typeof(IController).IsAssignableFrom(controllerType)) {
throw new ArgumentException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
controllerType),
"controllerType");
}
try {
return (IController)Activator.CreateInstance(controllerType);
}
catch (Exception ex) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_ErrorCreatingController,
controllerType),
ex);
}
}
//注意,它拋出的是 Http 404 的異常,就這么點差別,因此經(jīng)過改良,我也同樣的解決了這個問題,下面就是我定義的 ControllerFactory:
//UnityControllerFactory.cs:
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Reflection;
using Microsoft.Practices.Unity;
using System.Globalization;
/// <summary>
/// 依賴注入 ControllerFactory
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
public class UnityControllerFactory : DefaultControllerFactory
{
/// <summary>
/// 創(chuàng)建一個 Controller 類實例,覆蓋基類方法
/// </summary>
/// <param name="aRequestContext">Http請求上下文對象</param>
/// <param name="aControllerType">Controller類型</param>
/// <returns>
/// 返回 IController 類實例。
/// </returns>
/// <remarks>
/// 2010-10-09 [Max] 創(chuàng)建。
/// </remarks>
protected override IController GetControllerInstance(RequestContext aRequestContext, Type aControllerType)
{
//不適用的方式:
//if ( aControllerType == null )
//{
// throw new ArgumentNullException( "aControllerType" );
//}
//if ( !typeof( IController ).IsAssignableFrom( aControllerType ) )
//{
// throw new ArgumentException( string.Format( "{0} 不是 Controller。", aControllerType.Name ), "aControllerType" );
//}
//適用的方式:
if ( aControllerType == null )
{
throw new HttpException( 404, String.Format( CultureInfo.CurrentUICulture, "未發(fā)現(xiàn)指定的 Controller {0}。", aRequestContext.HttpContext.Request.Path ) );
}
if ( !typeof( IController ).IsAssignableFrom( aControllerType ) )
{
throw new ArgumentException( String.Format( CultureInfo.CurrentUICulture, "{0} 不是 Controller。", aControllerType ), "aControllerType" );
}
try
{
IUnityContainer container = GetContainer( aRequestContext );
return (IController) container.Resolve( aControllerType );
}
catch ( Exception ex )
{
throw new InvalidOperationException( String.Format( CultureInfo.CurrentUICulture, "無法創(chuàng)建 Controller {0}。", aControllerType ), ex );
}
}
/// <summary>
/// 獲取依賴注入容器對象
/// </summary>
/// <param name="aContext">Http請求上下文對象</param>
/// <returns>
/// 返回 IUnityContainer 類實例。
/// </returns>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
protected virtual IUnityContainer GetContainer( RequestContext aContext )
{
if ( aContext == null )
{
throw new ArgumentNullException( "aContext" );
}
var unityApp = aContext.HttpContext.ApplicationInstance as IUnityMvcApplication;
if ( unityApp == null )
{
throw new InvalidOperationException( "MvcHttpApplication 對象必須從 UnityMvcApplication 繼承。" );
}
IUnityContainer container = unityApp.Container;
if ( container == null )
{
throw new InvalidOperationException( "依賴注入容器對象無法訪問,請查看您的 MvcHttpApplication 類定義是否正確。" );
}
return container;
}
}
//IUnityMvcApplication.cs:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
/// <summary>
/// 依賴注入容器訪問接口
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
public interface IUnityMvcApplication
{
/// <summary>
/// 依賴注入容器屬性
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
IUnityContainer Container { get; }
}
// 下面這兩個單元的代碼,由于公司的特殊應用,封裝的比較死,不太好,不建議照搬,僅供參考。
//UnityMvcApplication.cs:
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Reflection;
using Microsoft.Practices.Unity;
/// <summary>
/// Mvc 應用程序類。
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
public class UnityMvcApplication : HttpApplication, IUnityMvcApplication
{
private static IUnityContainer _UnityContainer;
/// <summary>
/// 靜態(tài)依賴注入容器屬性
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
protected static IUnityContainer Container
{
get
{
if (_UnityContainer == null)
{
_UnityContainer = new UnityContainer();
}
return _UnityContainer;
}
}
/// <summary>
/// 實現(xiàn)IMvcApplication接口的依賴注入容器屬性
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
IUnityContainer IUnityMvcApplication.Container
{
get
{
return Container;
}
}
#region override methods
/// <summary>
/// 應用程序啟動事件
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
protected virtual void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
RegisterDependency();
RegisterControllerFactory();
}
/// <summary>
/// 應用程序結(jié)束事件
/// </summary>
/// <param name="sender">事件發(fā)起者</param>
/// <param name="e">事件參數(shù)</param>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
protected virtual void Application_End( object sender, EventArgs e )
{
CleanUp();
}
#endregion
#region protected virtual methods
/// <summary>
/// 注冊全局過濾器
/// </summary>
/// <param name="aFilters">全局過濾器集合</param>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
protected virtual void RegisterGlobalFilters(GlobalFilterCollection aFilters)
{
aFilters.Add(new HandleErrorAttribute());
}
/// <summary>
/// 注冊URL路由
/// </summary>
/// <param name="aRoutes">路由器對象</param>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
protected virtual void RegisterRoutes(RouteCollection aRoutes)
{
aRoutes.IgnoreRoute("{resource}.axd/{*pathInfo}");
aRoutes.MapRoute(
"Default", // Route name
"{controller}/{action}/{aId}", // URL with parameters
new { controller = "Home", action = "Index", aId = UrlParameter.Optional } // Parameter defaults
);
}
/// <summary>
/// 注冊依賴注入對象
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
protected virtual void RegisterDependency()
{
}
/// <summary>
/// 注冊Controller工廠
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
protected virtual void RegisterControllerFactory()
{
ControllerBuilder.Current.SetControllerFactory(typeof(UnityControllerFactory));
}
/// <summary>
/// 清除資源
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 創(chuàng)建。
/// </remarks>
protected virtual void CleanUp()
{
if (_UnityContainer != null)
{
_UnityContainer.Dispose();
}
}
#endregion
}
//Global.asax.cs:
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Reflection;
using Microsoft.Practices.Unity;
public class MvcApplication : UnityMvcApplication
{
protected override void Application_Start( )
{
base.Application_Start( );
}
protected override void RegisterDependency( )
{
Container.RegisterType<IRDict, DictRepository>( );
Container.RegisterType<IRStudent, StudentRepository>( );
}
}
//使用 DemoController.cs:
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
//using 你的Respository;
using Microsoft.Practices.Unity;
public class DemoController : BaseController
{
private IRDict _dictRepository;
private IRStudent _studentRepository;
public DemoController( IRDict aDictRespository, IRStudent aStudentRespository )
{
_dictRepository = aDictRespository;
_studentRepository = aStudentRespository;
}
}
上面的代碼都是從公司封裝的框架中提取出來,僅供大家分享。
由于 Unity 已經(jīng)包含在微軟 Enterprise Library 中,因此,只要下載安裝Enterprise Library安裝包即可。
其實我個人比較喜歡 NInject 又小、又輕便,但由于特殊原因,實在是沒有辦法,上面這些代碼很容易就改成 NInject。
Unity的地址: http://entlib.codeplex.com/
NInject的地址:http://ninject.org/download
浙公網(wǎng)安備 33010602011771號