標籤:play activator cheng ddd contains tab 官網 from turn
參考頁面:
http://www.yuanjiaocheng.net/webapi/create-crud-api-1-get.html
http://www.yuanjiaocheng.net/webapi/create-crud-api-1-post.html
http://www.yuanjiaocheng.net/webapi/create-crud-api-1-put.html
http://www.yuanjiaocheng.net/webapi/create-crud-api-1-delete.html
http://www.yuanjiaocheng.net/webapi/Consume-web-api.html
控制反轉(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 = "[email protected]"},
6: new Contact{ Id="002", Name = "李四", PhoneNo="456",EmailAddress = "[email protected]"}
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: }
IoC在ASP.NET Web API中的應用