標籤:
前言
閱讀本文之前,您也可以到Asp.Net Web API 2 系列導航進行查看 http://www.cnblogs.com/aehyok/p/3446289.html
本文主要來介紹在Asp.Net Web API使用Web API的Decpendency Resolver在控制器中如何注入依賴。
本文使用VS2013。本文的範例程式碼下載連結為http://pan.baidu.com/s/1BvFTs
為什麼要使用Dependency Resolver
一個dependency 其實就是一個對象或者另外一個對象需要的一個介面。例如,在Asp.Net Web API 2第二課——CRUD操作 http://www.cnblogs.com/aehyok/p/3434578.html中,我們定義了一個ProductsController的類,這個類需要一個IProductRepository 的執行個體,這個實現看起來像這樣:
public class ProductsController : ApiController{ private static IProductRepository repository = new ProductRepository(); // Controller methods not shown.}
這不是最好的設計,因為對於調用建立的ProductRepository
是通過在控制器中硬式編碼方式實現的。如果要使用IProductRepository的不同執行個體,我們將需要在ProductRepository中改變代碼。如果ProductsController
不依賴於任何具體執行個體的IProductRepository
那會是比較好的。
Dependency injection解決了這個問題。在Dependency injection中,對象是不會負責建立自己的依賴項的。相反,當你建立一個對象,注入這個依賴的時候是通過建構函式參數或者setter方法。
這裡是ProductsController中修改後的實現代碼:
public class ProductsController : ApiController{ private readonly IProductRepository repository; public ProductsController(IProductRepository repository) { if (repository == null) { throw new ArgumentNullException("repository"); } this.repository = repository; }
這樣是比較好的。現在可以切換到另外一個IProductRepository
的執行個體,而不用觸及到ProductsController的實現。
但是,在Asp.Net Web API中,你不能直接的建立一個控制器。相反,這個架構給你建立一個控制器,而且它並不知道IProductRepository
的相關資訊。這個架構也只能通過調用無參數的建構函式來建立你的控制器。
就在這個時候dependency resolver來了。dependency resolver的工作就是建立這個架構所需要的對象,包含congtrollers對象。通過提供一個自訂的dependency resolver,你可以代表架構來建立控制器執行個體。
一個簡單的dependency resolver
下面的代碼展示了一個簡單的dependency resolver。這個代碼主要只是展示了在Web API中依賴注入如何工作的。之後,我們將看到怎樣來合并一個Ioc的容器。
class SimpleContainer : IDependencyResolver{ static readonly IProductRepository respository = new ProductRepository(); public IDependencyScope BeginScope() { // This example does not support child scopes, so we simply return ‘this‘. return this; } public object GetService(Type serviceType) { if (serviceType == typeof(ProductsController)) { return new ProductsController(respository); } else { return null; } } public IEnumerable<object> GetServices(Type serviceType) { return new List<object>(); } public void Dispose() { // When BeginScope returns ‘this‘, the Dispose method must be a no-op. }}
一個 dependency resolver實現了這個IDependencyResolver 介面。這個IDependencyResolver 介面繼承了另外的兩個介面IDependencyScope 、IDisposable。
namespace System.Web.Http.Dependencies{ public interface IDependencyResolver : IDependencyScope, IDisposable { IDependencyScope BeginScope(); } public interface IDependencyScope : IDisposable { object GetService(Type serviceType); IEnumerable<object> GetServices(Type serviceType); }}
IDependencyScope 介面定義了兩個方法:
- GetService: 建立一個指定類型的執行個體
- GetServices: 建立一個指定類型的集合對象
對於控制器,這個架構調用 GetService來獲得控制器的單個執行個體。這就是我們簡單的容器建立控制器和注入repository。
對於你的dependency resolver不處理的任何類型,GetService 會返回null,GetServices 也會返回一個空的集合對象,尤其是,別拋出一個未知類型的異常。
這個IDependencyResolver 介面繼承了IDependencyScope ,添加了一個方法:
之後,我們將來討論嵌套的範圍內如何來管理我們對象的生命週期。現在,BeginScope 方法的實現我們簡單的返回一個this。
Setting the Dependency Resolver
現在在Web API全域設定物件中來設定Dependency Resolver。
主要是在Global.asax這個檔案當中。然後在Application_Start 方法中,將GlobalConfiguration.Configuration.DependencyResolver設定為你的Dependency Reslover。
public class WebApiApplication : System.Web.HttpApplication{ void ConfigureApi(HttpConfiguration config) { config.DependencyResolver = new SimpleContainer(); } protected void Application_Start() { ConfigureApi(GlobalConfiguration.Configuration); // ... }}
那麼現在你可以正常運行程式了。
範圍和對象聲明周期
控制器被建立的每個請求。為了協助管理對象的聲明周期,IDependencyResolver 使用了IDisposable介面。被添加到HttpConfiguration 上的dependency resolver對象擁有全域的範圍。當架構建立一個新的控制器執行個體的時候,它調用IDependencyResolver.BeginScope。這個方法返回一個IDependencyScope 。這個架構在IDependencyScope 上調用GetService 去獲得這個控制器。當架構處理完這個請求的時候,它在子範圍中調用Dispose 。你能通過Dispose 方法來釋放控制器的依賴。
Dependency Injection with IoC Containers
一個Ioc容器就是一個軟體組件,它負責建立依賴。Ioc容器為依賴注入提供公用的架構。如果你使用一個Ioc容器,你不需要在代碼中直接連同對象,幾個開源的.Net Ioc容器是可以利用的,例如Autofac, Castle Windsor, Ninject, Spring.NET, StructureMap 等等。
下面的例子我們來使用Unity,這個Ioc容器是由Microsoft patterns & practices開發的。
namespace ProductStore{ using System; using System.Collections.Generic; using System.Web.Http; using System.Web.Http.Dependencies; using Microsoft.Practices.Unity; class ScopeContainer : IDependencyScope { protected IUnityContainer container; public ScopeContainer(IUnityContainer container) { if (container == null) { throw new ArgumentNullException("container"); } this.container = container; } public object GetService(Type serviceType) { if (container.IsRegistered(serviceType)) { return container.Resolve(serviceType); } else { return null; } } public IEnumerable<object> GetServices(Type serviceType) { if (container.IsRegistered(serviceType)) { return container.ResolveAll(serviceType); } else { return new List<object>(); } } public void Dispose() { container.Dispose(); } } class IoCContainer : ScopeContainer, IDependencyResolver { public IoCContainer(IUnityContainer container) : base(container) { } public IDependencyScope BeginScope() { var child = container.CreateChildContainer(); return new ScopeContainer(child); } }}
這個ScopeContainer
類實現了IDependencyScope 代表了一個子範圍。這個IoCContainer
類實現了全域範圍內的依賴解析。並在BeginScope 方法中建立一個新的ScopeContainer對象。這個Unity 容器也有一個子容器的概念。因為我們可以用Unity 的子容器來初始化ScopeContainer
。這個ScopeContainer.Dispose
方法釋放了Unity的子容器。
下面的代碼用Unity註冊了controller和repository,然後設定Dependency resolver.
void ConfigureApi(HttpConfiguration config){ var unity = new UnityContainer(); unity.RegisterType<ProductsController>(); unity.RegisterType<IProductRepository, ProductRepository>( new HierarchicalLifetimeManager()); config.DependencyResolver = new IoCContainer(unity);}
每次HTTP請求的時候Web API 控制器被建立,然後請求被處理之後控制器被釋放。
現在同樣可以運行了。
總結
對依賴注入的研究,還沒有那麼深入,只知道簡單的怎麼用。
對於文中
public IDependencyScope BeginScope() { // This example does not support child scopes, so we simply return ‘this‘. return this; }
如果不適用this,那麼其他還可以使用什麼,還有待進一步的深入。之後自己還要對依賴Unity的依賴注入進行研究。不過感覺好像沒MEF那麼好用。
本文的參考連結為http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver
本文以同步到Web API系列導航中 http://www.cnblogs.com/aehyok/p/3446289.html
本文的範例程式碼下載連結為http://pan.baidu.com/s/1BvFTs
Asp.Net Web API 2第十一課——在Web API中使用Dependency Resolver