最近留意了一下ASP.NET MVC 的依赖注入,也看了不少相关的文章,自己也尝试了两种,分别为 NInject 和 Unity ,
在使用的过程中,也渐渐的了解了依赖注入的思想,于是从网上下载了一些相关的代码,直接拿来用之,包括来自微软官方的,
也有来自国外牛人博客的,但是使用当中也发生了一些问题,主要问题就是,当客户端请求一个不存在的Controller或者Action的时候
(甚至是请求一个不存在的图片或者资源),会产生异常,网上的大部分代码都会产生错误,这跟使用什么样的DI框架没有关系,
原因就出在覆盖 DefaultControllerFactory 的 GetControllerInstance 方法的实现上,当遇到不存的Controller 或者 Action 的时候,
抛出的是自定义的异常,而不是 HTTP 异常,于是打开了 MVC 的源码,仔细阅读了 DefaultControllerFactory.GetControllerInstance 的实现,
发现如下代码片断:
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 的异常,就这么点差别,因此经过改良,我也同样的解决了这个问题,下面就是我定义的 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] 创建。
/// </remarks>
public class UnityControllerFactory : DefaultControllerFactory
{
/// <summary>
/// 创建一个 Controller 类实例,覆盖基类方法
/// </summary>
/// <param name="aRequestContext">Http请求上下文对象</param>
/// <param name="aControllerType">Controller类型</param>
/// <returns>
/// 返回 IController 类实例。
/// </returns>
/// <remarks>
/// 2010-10-09 [Max] 创建。
/// </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, "未发现指定的 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, "无法创建 Controller {0}。", aControllerType ), ex );
}
}
/// <summary>
/// 获取依赖注入容器对象
/// </summary>
/// <param name="aContext">Http请求上下文对象</param>
/// <returns>
/// 返回 IUnityContainer 类实例。
/// </returns>
/// <remarks>
/// 2010-09-18 [Max] 创建。
/// </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] 创建。
/// </remarks>
public interface IUnityMvcApplication
{
/// <summary>
/// 依赖注入容器属性
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 创建。
/// </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] 创建。
/// </remarks>
public class UnityMvcApplication : HttpApplication, IUnityMvcApplication
{
private static IUnityContainer _UnityContainer;
/// <summary>
/// 静态依赖注入容器属性
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 创建。
/// </remarks>
protected static IUnityContainer Container
{
get
{
if (_UnityContainer == null)
{
_UnityContainer = new UnityContainer();
}
return _UnityContainer;
}
}
/// <summary>
/// 实现IMvcApplication接口的依赖注入容器属性
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 创建。
/// </remarks>
IUnityContainer IUnityMvcApplication.Container
{
get
{
return Container;
}
}
#region override methods
/// <summary>
/// 应用程序启动事件
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 创建。
/// </remarks>
protected virtual void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
RegisterDependency();
RegisterControllerFactory();
}
/// <summary>
/// 应用程序结束事件
/// </summary>
/// <param name="sender">事件发起者</param>
/// <param name="e">事件参数</param>
/// <remarks>
/// 2010-09-18 [Max] 创建。
/// </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] 创建。
/// </remarks>
protected virtual void RegisterGlobalFilters(GlobalFilterCollection aFilters)
{
aFilters.Add(new HandleErrorAttribute());
}
/// <summary>
/// 注册URL路由
/// </summary>
/// <param name="aRoutes">路由器对象</param>
/// <remarks>
/// 2010-09-18 [Max] 创建。
/// </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] 创建。
/// </remarks>
protected virtual void RegisterDependency()
{
}
/// <summary>
/// 注册Controller工厂
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 创建。
/// </remarks>
protected virtual void RegisterControllerFactory()
{
ControllerBuilder.Current.SetControllerFactory(typeof(UnityControllerFactory));
}
/// <summary>
/// 清除资源
/// </summary>
/// <remarks>
/// 2010-09-18 [Max] 创建。
/// </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 已经包含在微软 Enterprise Library 中,因此,只要下载安装Enterprise Library安装包即可。
其实我个人比较喜欢 NInject 又小、又轻便,但由于特殊原因,实在是没有办法,上面这些代码很容易就改成 NInject。
Unity的地址: http://entlib.codeplex.com/
NInject的地址:http://ninject.org/download