控制反轉(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: }
文章列表