IoC在ASP.NET Web API中的應用

bluepeach發表於2021-09-09

控制反轉(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 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 GetContacts(Predicate predicate);

   

   4: }

我們定義瞭如下一個DefaultContactRepository型別作為IContactRepository介面的預設實現者,簡單起見,我們採用一個靜態字典來儲存聯絡人列表。

   1: public class DefaultContactRepository : IContactRepository

   

   2: {

   

   3:     private static List contacts = new List

   

   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 GetContacts(Predicate predicate)

   

  10:     {

   

  11:         return contacts.Where(contact=>predicate(contact));

   

  12:     }

   

  13: }

我們在Global.asax中對自定義的UnityHttpControllerActivator進行了註冊。如下面的程式碼片斷所示,我們在Application_Start方法中建立了一個UnityContainer物件,並透過呼叫泛型方法RegisterType註冊了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();

   

   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框架作過多的介紹,有興趣的讀者可以訪問其官網(“”)瞭解Ninject。

   1: public class NinjectDependencyResolver : IDependencyResolver

   

   2: {

   

   3:     private List disposableServices = new List();

   

   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() where TTo : TFrom

   

  17:     {

   

  18:         this.Kernel.Bind().To();

   

  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 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來註冊介面與實現型別之間的對映關係。為了確保獲取的服務例項能夠被正常地釋放,我們定義了一個元素型別為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();

   

   8:         GlobalConfiguration.Configuration.DependencyResolver = dependencyResolver;

   

   9:     }

   

  10: }

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2558/viewspace-2810982/,如需轉載,請註明出處,否則將追究法律責任。

相關文章