Dependency Injection in ASP.NET Web API 2 (在web api2 中使用依赖注入)
2015-09-14 12:53
1026 查看
原文:http://www.asp.net/web-api/overview/advanced/dependency-injection
例如:我们定义一个Domain 对象
接下来用Entity Framework 来实现一个简单的仓储ProductRepository
然后,我们创建一个简单ProductController API 控制器,实现两个get 请求
看这个类的红色行代码,ProductsContoller 依赖了 ProductRepository ,ProductsContoller 在代码中创建了 ProductRepository 的实例,
用这种方式硬编码,是一个糟糕的注意(bad idea),因为:
1 ) 如果你想用不同实现来 替换ProductRepository ,必须修改 ProductsContoller 类 。(违反了开闭原则)
2 ) 如果ProductRepository 还有其他依赖,你需要在 ProductsContoller 类中实现其他的依赖项,一个大的项目通常都有很多controller ,这会造成大量的实现依赖分散在你的整个项目中。
3) 这很难进行单元测试,因为ProductRepository 已经硬编码在 ProductsContoller 中。对于单元测试,我们应该使用ProductRepository 的mock 或Sub (桩)实例,但这种写法无法进行单元测试。
我们要解决这个问题,只需把ProductRepository 注入到ProductsContoller 类中。 首先重构ProductRepository 实现一个 IProductRespository 接口
然后修改controller ,提供一个构造函数传递 IProductRepository 参数
这个例子使用了构造函数进行注入,你也可以使用属性或者方法进行注入。(个人建议在一个项目中,统一使用一种注入方式,例如都使用构造函数进行注入)。
现在有一个问题,因为我的程序并不能直接创建一个带参数的Controller。 Web API 程序收到一个路由请求时,创建一个controller ,但是它不知道如何创建一个带IProductRepository 参数的 controller。
到这儿,就该依赖注入登场了!
IDependencyScope 接口有两个方法
1) GetService 创建一个类型实例
2) GetServices 创建一个指定类型对象的集合。
IDependencyResolver 继承 IDependencyScope ,并且添加了一个BeginScope 方法,BeginScope 用来管理对象的生命周期。
当Web API 创建一个controller 实例时,它首先调用IDependencyResolver.GetService 方法,再传入controller 的参数类型。 我们可以使用这个可扩展的钩子 来创建Controller 的实例,解决依赖关系。
如果GetService 返回null,WebAPI 会在controller类 中寻找无参的构造函数。
Container 是一个软件组件,负责管理依赖。用Container 注册 类型,就可以使用Container 来创建类型的实例。容器会自动识别依赖关系,并且很多容器还可以控制对象的生命周期。
本教程,我们使用微软的 Unity 容器来解决依赖。
在包管理控制台中,输入如下命令,安装unity 。
接着再实现一个封装了unity 容器的 IDependencyResolver 接口实现类 。
注意:
1) 如果GetService 不能解析一个类型,那么必须返回 null
2) 如果GetServices 不能解析一个类型,那么必须返回一个空的集合实例。
不能解析类型时,按上面两种情况处理,不能抛出任何异常。
以上代码是写在 Global.asax 文件中的应用程序启动Application_Start() 方法中,
dependency resolver 依附 到HttpConfiguration 对象上,有一个全局范围(global scope)。当Web api 创建一个 controller 时, 它调用 BeginScope, 这个方法返回一个IDependencyScope 代表一个子范围。
Web api 在这个子范围中 调用 GetService 方法创建Controller ,当请求完成时,Web api 在这个子范围内调用Dispose 方法,用Dispose 这个方法来释放 Controller 的依赖。
你需要实现BeginScope 取决于你使用的容器,对于Unity ,BeginScope 的实现如下:
1 什么是依赖注入(Dependency Injection)
依赖,简单来说就是一个对象需要的任何其他对象。具体解释请Google或百度。在我们使用Web api2 开发应用程序时,通常都会遇到Controller 访问库 Repository 问题。例如:我们定义一个Domain 对象
public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } |
public class ProductsContext : DbContext { public ProductsContext() : base("name=ProductsContext") { } public DbSet<Product> Products { get; set; } } public class ProductRepository : IDisposable { private ProductsContext db = new ProductsContext(); public IEnumerable<Product> GetAll() { return db.Products; } public Product GetByID(int id) { return db.Products.FirstOrDefault(p => p.Id == id); } public void Add(Product product) { db.Products.Add(product); db.SaveChanges(); } protected void Dispose(bool disposing) { if (disposing) { if (db != null) { db.Dispose(); db = null; } } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } |
public class ProductsController : ApiController { // This line of code is a problem! 这行代码有问题 ProductRepository _repository = new ProductRepository(); public IEnumerable<Product> Get() { return _repository.GetAll(); } public IHttpActionResult Get(int id) { var product = _repository.GetByID(id); if (product == null) { return NotFound(); } return Ok(product); } } |
用这种方式硬编码,是一个糟糕的注意(bad idea),因为:
1 ) 如果你想用不同实现来 替换ProductRepository ,必须修改 ProductsContoller 类 。(违反了开闭原则)
2 ) 如果ProductRepository 还有其他依赖,你需要在 ProductsContoller 类中实现其他的依赖项,一个大的项目通常都有很多controller ,这会造成大量的实现依赖分散在你的整个项目中。
3) 这很难进行单元测试,因为ProductRepository 已经硬编码在 ProductsContoller 中。对于单元测试,我们应该使用ProductRepository 的mock 或Sub (桩)实例,但这种写法无法进行单元测试。
我们要解决这个问题,只需把ProductRepository 注入到ProductsContoller 类中。 首先重构ProductRepository 实现一个 IProductRespository 接口
public interface IProductRepository { IEnumerable<Product> GetAll(); Product GetById(int id); void Add(Product product); } public class ProductRepository : IProductRepository { // 省略接口的实现 } |
public class ProductsController : ApiController { private IProductRepository _repository; public ProductsController(IProductRepository repository) { _repository = repository; } // Other controller methods not shown. } |
现在有一个问题,因为我的程序并不能直接创建一个带参数的Controller。 Web API 程序收到一个路由请求时,创建一个controller ,但是它不知道如何创建一个带IProductRepository 参数的 controller。
到这儿,就该依赖注入登场了!
2 The Web API Dependency Resolver (Web API 依赖注入)
Web API 定义了一个IDependencyResolver 接口来 解析依赖,定义如下:public interface IDependencyResolver : IDependencyScope, IDisposable { IDependencyScope BeginScope(); } public interface IDependencyScope : IDisposable { object GetService(Type serviceType); IEnumerable<object> GetServices(Type serviceType); } |
1) GetService 创建一个类型实例
2) GetServices 创建一个指定类型对象的集合。
IDependencyResolver 继承 IDependencyScope ,并且添加了一个BeginScope 方法,BeginScope 用来管理对象的生命周期。
当Web API 创建一个controller 实例时,它首先调用IDependencyResolver.GetService 方法,再传入controller 的参数类型。 我们可以使用这个可扩展的钩子 来创建Controller 的实例,解决依赖关系。
如果GetService 返回null,WebAPI 会在controller类 中寻找无参的构造函数。
3 Dependency Resolution with the Unity Container (使用Unity容器来解决依赖)
虽然我们可以从头写完一个IDependencyResolver 的实现,但这个接口的真正设计目的,是充当Web api 和 现存Container 的桥梁。Container 是一个软件组件,负责管理依赖。用Container 注册 类型,就可以使用Container 来创建类型的实例。容器会自动识别依赖关系,并且很多容器还可以控制对象的生命周期。
本教程,我们使用微软的 Unity 容器来解决依赖。
在包管理控制台中,输入如下命令,安装unity 。
Install-Package Unity |
using Microsoft.Practices.Unity; using System; using System.Collections.Generic; using System.Web.Http.Dependencies; 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(); } } |
1) 如果GetService 不能解析一个类型,那么必须返回 null
2) 如果GetServices 不能解析一个类型,那么必须返回一个空的集合实例。
不能解析类型时,按上面两种情况处理,不能抛出任何异常。
4 Configuring the Dependency Resolver 配置依赖解析器
最后一步,在全局 HttpConfiguration 对象的[b]DependencyResolver 属性上设置 依赖解析器[/b]public static void Register(HttpConfiguration config) { //1 创建容器 var container = new UnityContainer(); // 2 注册组件 container.RegisterType<IProductRepository, ProductRepository>(new HierarchicalLifetimeManager()); //3 配置依赖解析用 unity容器 config.DependencyResolver = new UnityResolver(container); // Other Web API configuration not shown. } |
public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } } |
5 Dependenecy Scope and Controller Lifetime 依赖范围和控制器的生命周期
每个request都会创建一个controller ,为了管理对象的生命周期,IDependencyResolver 使用一个 Scope 的概念。dependency resolver 依附 到HttpConfiguration 对象上,有一个全局范围(global scope)。当Web api 创建一个 controller 时, 它调用 BeginScope, 这个方法返回一个IDependencyScope 代表一个子范围。
Web api 在这个子范围中 调用 GetService 方法创建Controller ,当请求完成时,Web api 在这个子范围内调用Dispose 方法,用Dispose 这个方法来释放 Controller 的依赖。
你需要实现BeginScope 取决于你使用的容器,对于Unity ,BeginScope 的实现如下:
public IDependencyScope BeginScope() { var child = container.CreateChildContainer(); return new UnityResolver(child); } |
相关文章推荐
- asp 中文乱码问题解决方法
- asp实现excel中的数据导入数据库
- ASP.NET与Ajax的实现方式小总结
- 『Asp.Net 组件』Asp.Net 服务器组件 内嵌CSS:将CSS封装到程序集中
- 『Asp.Net 组件』Asp.Net 服务器组件 内嵌CSS:将CSS封装到程序集中
- ASP.NET零碎
- ASP.NET MVC5网站开发项目框架(二)
- ASP.NET MVC5 网站开发框架模型、数据存储、业务逻辑(三)
- ASP.NET MVC 5之邮件服务器与客户端
- .NET基础 (21)ASP NET应用开发
- 12、ASP.NET MVC入门到精通——HtmlHelper
- ASP.NET的新成员ASP.NET WebHooks
- [Raspbian]RaspberryPi做自动pppoe拨号路由器
- 利用Aspose.Words打印word文档
- 使用VS Code开发ASP.NET 5 应用程序
- ASP.NET运行时报 对象名 'users' 无效 错误信息
- asp.net mvc 动态显示不同的部分视图
- ASP.NET MVC 路由机制
- Asp.Net MVC 合并js或css请求
- 三张图片解析ASP.net的整个生命周期