什么是依赖注入
依赖,就是一个对象需要的另一个对象,比如说,这是我们通常定义的一个用来处理数据访问的存储,让我们用一个例子来解释,首先,定义一个领域模型如下:
namespace Pattern.DI.MVC.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } }
然后是一个用于实例的简单存储类:
namespace Pattern.DI.MVC.Models { public class ProductContext { public List<Product> Products { get; internal set; } public ProductContext() { Products.Add(new Product() {Id = 1, Name = "苍阿姨", Price = 100}); Products.Add(new Product() {Id = 2, Name = "武藤奶奶", Price = 200}); Products.Add(new Product() {Id = 3, Name = "小泽姐姐", Price = 300}); } } public class ProductRepository { private ProductContext context=new ProductContext(); public IEnumerable<Product> GetAll() { return context.Products; } public Product GetById(int id) { return context.Products.FirstOrDefault(p => p.Id == id); } } }
现在,我们定义一个ASP.NET Web API控制器来支持对Product实体集的GET请求:
namespace Pattern.DI.MVC.Controllers { public class ProductController : ApiController { private readonly ProductRepository productRepository=new ProductRepository(); public IEnumerable<Product> Get() { return productRepository.GetAll(); } public Product Get(int id) { return productRepository.GetById(id); } } }
现在注意到,这个控制器依赖了“ProductRepository”这个类,我们在类中实例化了ProductRepository,这就是设计的“坏味道”了,因为如下几个原因:
- 假如你想要使用另外一个实现替换ProductRepository,你还要去修改ProductController类;
- 假如ProductRepository存在依赖,你必须在ProductController中配置他们,对于一个拥有很多控制器的大项目来说,你就配置工作将深入到任何可能的地方;
- 这是很难去做单元测试的因为控制器中硬编码了对数据库的查询,对于一个单元测试,你可以在没有确切设计之前,使用一个仿制的桩存储体。
我们可以使用注入一个ProductRepsoitory来解决这个问题,首先重构ProductRepository的方法到一个接口中:
namespace Pattern.DI.MVC.Models { public interface IProductRepository { IEnumerable<Product> GetAll(); Product GetById(int id); } public class ProductRepository:IProductRepository { private ProductContext context = new ProductContext(); public IEnumerable<Product> GetAll() { return context.Products; } public Product GetById(int id) { return context.Products.FirstOrDefault(p => p.Id == id); } } }
namespace Pattern.DI.MVC.Controllers { public class ProductController : ApiController { private readonly IProductRepository productRepository; public ProductController(IProductRepository productRepository) { this.productRepository = productRepository; } public IEnumerable<Product> Get() { return productRepository.GetAll(); } public Product Get(int id) { return productRepository.GetById(id); } } }
这个示例使用了构造器注入,你同样可以使用设置器注入的方式,ASP.NET Web API在为请求映射了路由之后创建控制器,而且现在他不知道任何关于IProductRepository的细节,这是通过API依赖器解析到的。
ASP.NET Web API依赖解析器
ASP.NET Web API定义了一个IDependencyResolever用来解析依赖项目,以下是这个接口的定义:
public interface IDependencyResolver : IDependencyScope, IDisposable { IDependencyScope BeginScope(); } public interface IDependencyScope : IDisposable { object GetService(Type serviceType); IEnumerable<object> GetServices(Type serviceType); }
这个接口有两个方法
- GetService为一个类型创建一个实例;
- GetServices为一个特定的类型创建一个实例集合
这个接口继承自IDependencyScope并且添加了BeginScope方法,在这篇文章接下来将讨论这个方法。
当ASP.NET Web API创建一个controller实例的时候,它首先调用IDependencyResolver的GetService方法,传回一个Controller实例,你可以使用一个扩展的钩子去创建控制器并且解析依赖。假如GetService方法返回NULL,ASP.NET Web API将查找一个无参的构造函数。
使用Unity解析依赖
虽然你可以重头开始写一个IDenpendencyResolver的实现,但是这个接口已经设计了可以作为ASP.NET Web API和IoC工具的桥梁。
IoC容器是一个用来管理依赖项目的组建,你可以在其中注册类型,在使用的时候创建对象,IoC容易自动解析出依赖的关系,许多IoC容器允许你在对象的生命周期中进行控制。
首先在项目中使用NuGet Package Manage Console安装Unity,关于Unity的介绍可以点击这里查看详细。
Install-Package Unity
以下是一个使用Unity容器对IDependencyResolver的实现:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using Microsoft.Practices.Unity; using System.Web.Http.Dependencies; namespace Pattern.DI.MVC.Models { public class UnityResolver : IDependencyResolver { protected IUnityContainer container; public UnityResolver(IUnityContainer container) { if (container == null) { throw new ArgumentNullException("container"); } this.container = container; } public object GetService(Type serviceType) { try { return container.Resolve(serviceType); } catch (ResolutionFailedException) { return null; } } public IEnumerable<object> GetServices(Type serviceType) { try { return container.ResolveAll(serviceType); } catch (ResolutionFailedException) { return new List<object>(); } } public IDependencyScope BeginScope() { var child = container.CreateChildContainer(); return new UnityResolver(child); } public void Dispose() { container.Dispose(); } } }
配置依赖解析
在全局的HttpConfiguration对象中DependencyResolver属性上设置依赖解析器,以下的代码使用Unity注册IProductRepository接口并且创建一个UnityResolver,修改App_Start/WebApiConfig.cs中的Register方法
namespace Pattern.DI.MVC { public static class WebApiConfig { public static void Register(HttpConfiguration config) { var container = new UnityContainer(); container.RegisterType<IProductRepository, ProductRepository>(); config.DependencyResolver = new UnityResolver(container); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
至此完工,测试Api返回数据
原文地址:http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver