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之前的关系基本上可以通过如下图所示的类图来体现。
五、通过自定义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: }