ASP.NET MVC Controller激活系统详解:IoC的应用[下篇]

[上篇]除了通过自定义ControllerFactory的方式引入IoC之外,在使用默认DefaultControllerFactory情况下也可以通过一些扩展使基于IoC的Controller激活成为可能。主要的方式就是自定义ControllerActivator和 DependencyResolver。

四、ControllerActivator V.S. DependencyResolver

如下面的代码片断所示,DefaultControllerFactory具有两个构造函数重载,其中一个具有一个类型为IControllerActivator接口的参数,我们将实现了该接口得类型统称为ControllerActivator。

   1: public class DefaultControllerFactory : IControllerFactory
   2: {
   3:     //其他成员
   4:     public DefaultControllerFactory();
   5:     public DefaultControllerFactory(IControllerActivator controllerActivator);   
   6: }

顾名思义,ControllerActivator就是Controller的“激活器”,Controller的激活实现在唯一的Create方法中。如下面的代码所示,该方法具有两个参数(requestContext和controllerType),分别代表当前请求上下文和解析出来的目标Controller的类型。

   1: public interface IControllerActivator
   2: {
   3:     IController Create(RequestContext requestContext, Type controllerType);
   4: }

在默认的情况下(调用DefaultControllerFactory默认构造函数或者指定的参数为Null),Controller激活系统 会默认使用一个类型为DefaultControllerActivator的对象。如下面的代码片断所示,DefaultControllerActivator是一个实现了IControllerActivator私有类型而已,我们不能直接通过编程的方式使用它。

   1: private class DefaultControllerActivator : IControllerActivator
   2: {
   3:     public DefaultControllerActivator();
   4:     public DefaultControllerActivator(IDependencyResolver resolver);
   5:      public IController Create(RequestContext requestContext,  Type controllerType);
   6: }

DefaultControllerActivator的构造函数具有一个类型为IDependencyResolver的参数,这是一个重要的接口,我们将实现了该接口的类型统称为DependencyResolver。。如下面的代码片断所示,IDependencyResolver接口具有两个方法GetService和GetServices,用于根据指定的类型获取单个或者多个实例。实际上DefaultControllerActivator就是通过调用GetService方法获取具体的Controller对象的

   1: public interface IDependencyResolver
   2: {
   3:     object GetService(Type serviceType);
   4:     IEnumerable<object> GetServices(Type serviceType);
   5: }

如果在构造DefaultControllerActivator对象的时候传入的参数为Null,那么Controller激活系统会使用通过DependencyResolver的静态只读属性Current表示DependencyResolver。需要提醒的是,DependencyResolver类型没有实现IDependencyResolver接口,而是对一个实现了IDependencyResolver接口类型对象的封装。

   1: public class DependencyResolver
   2: {
   3: private static DependencyResolver _instance;
   4:  
   5:     public void InnerSetResolver(object commonServiceLocator);
   6:     public void InnerSetResolver(IDependencyResolver resolver);
   7:     public void InnerSetResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices);
   8:     
   9:     public static void SetResolver(object commonServiceLocator);
  10:     public static void SetResolver(IDependencyResolver resolver);
  11:     public static void SetResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices);
  12:  
  13:     public static IDependencyResolver Current { get; }
  14:     public IDependencyResolver InnerCurrent { get; }
  15: }

这个被封装的DependencyResolver(指实现了接口IDependencyResolver的某个类型的类型,不是指DependencyResolver类型的对象,对于后者我会采用“DependencyResolver类型对象”的说法)通过只读属性InnerCurrent表示,而三个InnerSetResolver方法重载用于初始化改属性。静态字段_instance表示当前的DependencyResolver类型对象,静态只读属性Current则表示该对象内部封装的DependencyResolver对象,而它通过三个静态的SetResolver进行初始化。

如果我们不曾通过调用DependencyResolver的静态方法SetResolver通过Current属性表示的当前DependencyResolver进行显示设置,该属性默认返回一个DefaultDependencyResolver对象。如下面的代码片断所示,DefaultDependencyResolver是一个实现了IDependencyResolver接口的私有类型,在实现的GetService方法中,它直接通过根据指定的类型以反射的形式创建相应的对象并返回,所以前面我们说DefaultControllerFactory根据解析出来的Controller类型以反射的形式创建对应的实例在这里得到了印证。至于GetServices方法则返回一个空对象集合。

   1: private class DefaultDependencyResolver : IDependencyResolver
   2: {
   3:     public object GetService(Type serviceType)    
   4:     {
   5:         if (serviceType.IsInterface || serviceType.IsAbstract)
   6:         {
   7:             return null;
   8:         }
   9:         try
  10:         {
  11:             return Activator.CreateInstance(serviceType);
  12:         }
  13:         catch
  14:         {
  15:             return null;
  16:         }
  17:     }
  18:  
  19:     public IEnumerable<object> GetServices(Type serviceType)
  20:     {
  21:         return Enumerable.Empty<object>();
  22:     }
  23: }

上面介绍的类型DefaultControllerFactory、IControllerActivator、DefaultControllerActivator、IDependencyResolver、DefaultDependencyResolver和DependencyResolver之前的关系基本上可以通过如下图所示的类图来体现。

image

五、通过自定义ControllerActivator实现IoC

如果我们基于一个ControllerActivator对象来创建一个DefaultControllerFactory,它会最终被用于Controller对象的激活,那么我们可以自定义ControllerActivator的方式将IoC引入Controller激活系统。我们接下来自定义的ControllerActivtor基于另一个IoC框架Ninject,较之Unity,Ninject是一个更加轻量级也更适合ASP.NET MVC的IoC框架。我们将自定义的ControllerActivator起名为NinjectControllerActivator,全部定义如下。[源代码从这里下载]

   1: public class NinjectControllerActivator: IControllerActivator
   2: {
   3:     public IKernel Kernel { get; private set; }
   4:     public NinjectControllerActivator()
   5:     {
   6:         this.Kernel = new StandardKernel();
   7:         AddBindings();    
   8:     }    
   9:     public IController Create(RequestContext requestContext, Type controllerType)
  10:     {
  11:         return (IController)this.Kernel.TryGet(controllerType) as IController;
  12:     }
  13:     private void AddBindings()
  14:     {
  15:         this.Kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>();
  16:     }
  17: }

我们使用的还是上面演示的关于员工管理的例子。NinjectControllerActivator的只读属性Kernel在这里用于类型注册和基于类型的实例提供,具体来说它是在构造函数中初始化的StandardKernel对象。同样在构造函数中,我们通过该Kernel实现了作为Model接口的IEmployeeRepository类型和Model实现的EmployeeRepository类型之间的映射。在Create方法中,我们通过Kernel的TryGet方法根据指定的类型获取相应的Controller对象。

现在我们无须再使用自定义的ControllerFactory,只需要注册一个基于我们自定义的NinjectControllerActivator的DefaultControllerFactory即可。定义在Global.asax中与ControllerFactory注册相关的代码如下所示。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     //其他成员
   4:     protected void Application_Start()
   5:     {
   6:         //其他操作
   7:         NinjectControllerActivator controllerActivator =  new NinjectControllerActivator();
   8:         DefaultControllerFactory controllerFactory  = new DefaultControllerFactory(controllerActivator);
   9:         ControllerBuilder.Current.SetControllerFactory(controllerFactory);
  10:     }
  11: }

六、通过自定义DependencyResoolver实现IoC

通过前面的介绍我们知道,当我们调用构造函数创建一个DefaultControllerFactory的时候,如果调用的时候默认无参构造函数,后者将作为参数的ControllerActivator对象设置为Null,那么默认请求用于激活Controller实例的是通过DependencyResoolver类型的静态属性Current表示的DependencyResoolver对象。换言之,我们可以通过自定义DependencyResoolver的方式来实现基于IoC的Controller激活。

同样是采用Ninject,我们定义了一个具有如下定义的NinjectDependencyResolver。与上面定义的NinjectControllerActivator类似,NinjectDependencyResolver具有一个IKernel类型的只读属性Kernel,该属性在构造函数中被初始化。同样是在构造函数中,我们通过该Kernel完成了IEmployeeRepository接口和EmployeeRepository类型的注册。对于实现的GetService和GetServices方法,我们直接调用Kernel的TryGet和GetAll返回指定类型的实例和实例列表。[源代码从这里下载]

   1: public class NinjectDependencyResolver : IDependencyResolver
   2: {
   3:     public IKernel Kernel { get; private set; }
   4:     public NinjectDependencyResolver()
   5:     {
   6:         this.Kernel = new StandardKernel();
   7:         AddBindings();
   8:     }
   9:     private void AddBindings()
  10:     {
  11:         this.Kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>();
  12:     }
  13:  
  14:     public object GetService(Type serviceType)
  15:     {
  16:         return this.Kernel.TryGet(serviceType);
  17:     }
  18:  
  19:     public IEnumerable<object> GetServices(Type serviceType)
  20:     {
  21:         return this.Kernel.GetAll(serviceType);
  22:     }
  23: }

由于默认情况下通过无参构造函数创建的DefaultConrtollerFactory会被使用,所以我们无须进行ControllerFactory的注册。我们只需要创建一个自定义的NinjectDependencyResolver对象并将其作为当前的DependencyResolver即可,定义在Global.asax设置当前DependencyResolver的代码如下所示。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     //其他成员
   4:     protected void Application_Start()
   5:     {
   6:         //其他操作
   7:         DependencyResolver.SetResolver( new NinjectDependencyResolver());
   8:     }
   9: }
 
ASP.NET MVC Controller激活系统详解:总体设计
ASP.NET MVC Controller激活系统详解:默认实现
ASP.NET MVC Controller激活系统详解:IoC的应用[上篇]
ASP.NET MVC Controller激活系统详解:IoC的应用[下篇]
posted @ 2012-04-01 12:33 Artech 阅读(...) 评论(...) 编辑 收藏