前言
閱讀本文之前,您也可以到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: 創建一個嵌套的范圍
之后,我們將來討論嵌套的范圍內如何來管理我們對象的生命周期。現在,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