[上篇]除了通過自定義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: }