文章出處

控制反轉(Inversion of Control,IoC),簡單地說,就是應用本身不負責依賴對象的創建和維護,而交給一個外部容器來負責。這樣控制權就由應用轉移到了外部IoC容器,控制權就實現了所謂的反轉。比如在類型A中需要使用類型B的實例,而B實例的創建并不由A來負責,而是通過外部容器來創建。通過IoC的方式實現針對目標HttpController的激活具有重要的意義。[本文已經同步到《How ASP.NET Web API Works?》]

一、 基于IoC的HttpControllerActivator

將IoC應用于HttpController激活系統的目的在于讓一個預定義的IoC容器來提供最終的HttpController對象。通過《ASP.NET Web API的Controller是如何被創建的?》的介紹我們知道HttpController的激活最終由HttpControllerActivator對象來完成,所以將IoC與ASP.NET Web API的HttpController激活系統進行集成最為直接的方式莫過于自定義一個HttpControllerActivator。

我們通過一個簡單實例來演示如何通過自定義HttpControllerActivator的方式實現與IoC的集成,我們采用的IoC框架是Unity。我們在一個ASP.NET Web API應用中定義了這個UnityHttpControllerActivator類型。UnityHttpControllerActivator具有一個表示Unity容器的屬性UnityContainer,該屬性在構造函數中被初始化。在用于創建的HttpController的Create方法中,我們調用此UnityContainer對象的Resolve方法創建目標HttpController對象。

   1: public class UnityHttpControllerActivator : IHttpControllerActivator
   2: {
   3:     public IUnityContainer UnityContainer { get; private set; }
   4:  
   5:     public UnityHttpControllerActivator(IUnityContainer unityContainer)
   6:     {        
   7:         this.UnityContainer = unityContainer;
   8:     }
   9:  
  10:     public  IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
  11:     {
  12:         return (IHttpController)this.UnityContainer.Resolve(controllerType);
  13:     }
  14: }

接下來我們定義了如下一個繼承自ApiController的ContactsController來管理聯系人信息。簡單起見,我們只定義了唯一的Action方法Get用于獲取聯系人信息。該方法具有一個可缺省的參數id表示希望獲取的聯系人的ID,如果沒有提供此參數則返回所有聯系人列表。

   1: public class ContactsController : ApiController
   2: {
   3:     public IContactRepository Repository { get; private set; }
   4:     public ContactsController(IContactRepository repository)
   5:     {
   6:         this.Repository = repository;
   7:     }
   8:     public IEnumerable<Contact> Get(string id = "")
   9:     {
  10:         return this.Repository.GetContacts(contact => 
  11:             string.IsNullOrEmpty(id) || id == contact.Id);
  12:     }
  13: }
  14:  
  15: public class Contact
  16: {
  17:     public string Id { get; set; }
  18:     public string Name { get; set; }
  19:     public string PhoneNo { get; set; }
  20:     public string EmailAddress { get; set; }
  21:     public string Address { get; set; }
  22: }

Action方法利用Repository屬性返回的對象來實施聯系人的查詢工作,這個IContactRepository接口類型的屬性在構造函數中初始化。我們利用IContactRepository接口來抽象對聯系人數據的存儲,如下面的代碼片斷所示,我們在此接口中僅定義了唯一的GetContacts方法根據指定的添加來篩選對應的聯系人列表。

   1: public interface IContactRepository
   2: {
   3:     IEnumerable<Contact> GetContacts(Predicate<Contact> predicate);
   4: }

我們定義了如下一個DefaultContactRepository類型作為IContactRepository接口的默認實現者,簡單起見,我們采用一個靜態字典來保存聯系人列表。

   1: public class DefaultContactRepository : IContactRepository
   2: {
   3:     private static List<Contact> contacts = new List<Contact>
   4:     {
   5:         new Contact{ Id="001", Name = "張三",  PhoneNo="123", EmailAddress = "zhangsan@gmail.com"},
   6:         new Contact{ Id="002", Name = "李四",  PhoneNo="456",EmailAddress = "lisi@gmail.com"}
   7:     };
   8:  
   9:     public IEnumerable<Contact> GetContacts(Predicate<Contact> predicate)
  10:     {
  11:         return contacts.Where(contact=>predicate(contact));
  12:     }
  13: }

我們在Global.asax中對自定義的UnityHttpControllerActivator進行了注冊。如下面的代碼片斷所示,我們在Application_Start方法中創建了一個UnityContainer對象,并通過調用泛型方法RegisterType<TFrom,TTo>注冊了IContactRepository接口和DefaultContactRepository類型之間的匹配關系。我們最后根據這個UnityContainer創建一個UnityHttpControllerActivator對象,并將其注冊到當前ServicesContainer上。

   1: public class WebApiApplication: System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         //其他操作
   6:         IUnityContainer unityContainer = new UnityContainer();
   7:         unityContainer.RegisterType<IContactRepository,   DefaultContactRepository>(); 
   8:         GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new UnityHttpControllerActivator(unityContainer));
   9:     }
  10: }

當此ASP.NET Web API應用運行之后,我們可以直接在瀏覽器中輸入相應的地址獲取所有聯系人列表(“/api/contacts”)和針對某個ID為“001”(“/api/contacts/001”)的聯系人信息,相應的聯系人信息會以如下圖所示的形式出現在瀏覽器上。

二、基于IoC的DependencyResolver

由于默認的DefaultHttpControllerActivator會先利用當前注冊的DependencyResolver對象去激活目標HttpController,所以除了利用自定義的HttpControllerActivator將IoC引入HttpController激活系統之外,另一個有效的方案就是注冊自定義的DependencyResolver。

接下來將要自定義的DependencyResolver基于另一個叫作“Ninject”的IoC框架。較之Unity,Ninject是一個更加輕量級的IoC框架。篇幅所限,我們不便對這個IoC框架作過多的介紹,有興趣的讀者可以訪問其官網(“http://www.ninject.org/”)了解Ninject。

   1: public class NinjectDependencyResolver : IDependencyResolver
   2: {
   3:     private List<IDisposable> disposableServices = new List<IDisposable>();
   4:     public IKernel Kernel { get; private set; }
   5:  
   6:     public NinjectDependencyResolver(NinjectDependencyResolver parent)
   7:     {
   8:         this.Kernel = parent.Kernel;
   9: }
  10:  
  11:     public NinjectDependencyResolver()
  12:     {
  13:         this.Kernel = new StandardKernel();
  14:     }
  15:  
  16:     public void Register<TFrom, TTo>() where TTo : TFrom
  17:     {
  18:         this.Kernel.Bind<TFrom>().To<TTo>();
  19: }
  20:  
  21:     public IDependencyScope BeginScope()
  22:     {
  23:         return new NinjectDependencyResolver(this);
  24:     }
  25:  
  26:     public object GetService(Type serviceType)
  27:     {
  28:         return this.Kernel.TryGet(serviceType);
  29:     }
  30:  
  31:     public IEnumerable<object> GetServices(Type serviceType)
  32:     {
  33:         foreach (var service in this.Kernel.GetAll(serviceType))
  34:         {
  35:             this.AddDisposableService(service);
  36:             yield return service;
  37:         }
  38: }    
  39:  
  40:     public void Dispose()
  41:     {
  42:         foreach (IDisposable disposable in disposableServices)
  43:         {
  44:             disposable.Dispose();
  45:         }
  46: }
  47:  
  48:     private void AddDisposableService(object servie)
  49:     {
  50:         IDisposable disposable = servie as IDisposable;
  51:         if (null != disposable && !disposableServices.Contains(disposable))
  52:         {
  53:             disposableServices.Add(disposable);
  54:         }
  55:     }
  56: }

我們創建了如上一個類型為NinjectDependencyResolver的自定義DependencyResolver。NinjectDependencyResolver的核心是類型為IKernel的只讀屬性Kernel,用于獲取服務實例的GetService和GetServices方法分別通過調用此Kernel屬性的TryGet和GetAll方法來實現。BeginScope方法返回一個新的NinjectDependencyResolver對象,它與自身擁有同一個Kernel對象。我們定義了額外的方法Register<TFrom,TTo>來注冊接口與實現類型之間的映射關系。為了確保獲取的服務實例能夠被正常地釋放,我們定義了一個元素類型為IDisposable的列表。如果獲取的對象實現了IDisposable接口,它會被放入這個列表中,我們在實現的Dispose方法中釋放該列表中的所有對象。

現在我們將這個自定義的NinjectDependencyResolver應用到上一個演示實例中。我們只需要將Global.asax中針對自定義HttpControllerActivator的注冊替換成針對NinjectDependencyResolver的注冊即可。運行此ASP.NET Web API應用后通過瀏覽器試圖獲取聯系人信息,我們依然會得到如上圖所示的結果。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         //其他操作
   6:         NinjectDependencyResolver dependencyResolver = new NinjectDependencyResolver();
   7:         dependencyResolver.Register<IContactRepository, DefaultContactRepository>();
   8:         GlobalConfiguration.Configuration.DependencyResolver = dependencyResolver; 
   9:     }
  10: }

文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()