使用 Unity 进行依赖注入
在面向对象的编程模式中,使用对象模型协同工作。这会在对象和组件之间产生依赖关系,在项目复杂性增加时这种依赖关系就变得难以管理。通常我们会使用接口或工厂模式来实现解耦,但这只能解决部分问题,有时候反而会使问题变得更糟(本来直接依赖的两个组件现在又依赖新增的接口)
依赖注入
依赖注入 (Dependency Injection, DI) 是控制反转的实现。控制反转 (Inversion of Control, IoC) 意味着对象不会创建他们依赖的其他组件,而是从外部源获取所需的对象。一般通过构造器或属性进行依赖注入。
使用依赖注入和控制反转的优点有:
- 减少类耦合
- 增加代码重用
- 提高代码可维护性
- 改进了应用程序测试
演示
创建项目
为了演示如何使用 Unity 进行依赖注入,这里创建一个名为 BookStore.API 的空项目,并添加 ASP.NET WebApi 核心引用。为了保持数模型实体与存储接收逻辑之间的分离,我又创建了一个名为 BookStore.Domain 的类库项目,并在 BookStore.API 项目中添加了对 BookStore.Domain 的引用
添加 NuGet 包
为 BookStore.API 和 BookStore.Domain 添加 EntityFramework 包
1 | Install-Package EntityFramework |
因为创建的是 ASP.NET WebApi 项目,所以我们需要添加 Unity.AspNet.WebApi 包到项目中以使用 Unity 进行依赖注入:
1 | Install-Package Unity.AspNet.WebApi |
包管理器会自动添加相应的文件和引用
*如果创建的是 ASP.NET MVC 项目,则需要添加 Install-Package Unity.Mvc 包到项目中
依赖注入
首先,我在 BookStore.Domain 项目中使用了 代码先行 (Code-First) 模式,这样做的好处是 EntityFramework 会在应用程序运行的时候根据模型类生成数据库。然后,我又在 BookStore.Domain 中实现了 存储库 (Repository) 模式。接下来在 BookStore.API 中添加一个名为 Home 的 Web API 控制器
1 | using System; |
虽然这里使用了 IBookInfoRepository 接口来建立松耦合,但是还是必须在 HomeController 中实例化 BookInfoRepository。接下来我们改为使用 Unity 通过构造函数将 BookInfoRepository 注入进来,这样就能使 IBookInfoRepository 接口和 BookInfoRepository 实现之间完全解耦
1 | using System; |
接下来在 App_Start/UnityConfig.cs 文件的 RegisterTypes 方法中配置注入规则
1 | public static void RegisterTypes(IUnityContainer container) |
然后在浏览器中访问地址 http://localhost:7529/api/Home/GetBookInfos 如果一切正常你将得到接口的返回值
1 | <ArrayOfBookInfo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/BookStore.Domain.Models"> |
创建 Help Page
因为这是一个 ASP.NET WebApi 项目,所以我们顺带着创建 API 帮助页
先添加 Microsoft.AspNet.WebApi.HelpPage 包到 BookStore.API 项目中
1 | Install-Package Microsoft.AspNet.WebApi.HelpPage |
包管理器会在项目中创建 Areas/HelpPage 文件夹,找到 HelpPage/App_Start/HelpPageConfig.cs 文件中的 Register 方法,将以下语句的注释取消
1 | config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/BookStore.API.xml"))); |
设置 BookStore.API 项目属性,修改 生成 → 输出 → XML 文档文件 的值为 bin\BookStore.API.xml 以和 Register 方法中设置的值对应
最后在项目根目录的 Global.asax 文件中的 Application_Start 方法中注册区域
1 | protected void Application_Start() |
注意事项
- Code-First 模式会自动创建数据库,创建的数据库默认存储在 %USERPROFILE% 文件夹下,要删除数据库以重新创建务必从 SQL Server 对象资源管理器 中操作
- 如果创建了一个同时包含 ASP.NET WebApi 和 ASP.NET MVC 核心引用的项目,则会自动在 Application_Start 方法中注册 MVC 项目的路由规则
1 | void Application_Start(object sender, EventArgs e) |
若在这个项目中又同时使用了 Unity 进行依赖注入则 Help Page 将不能正常生成,解决方案是在 App_Start/UnityConfig.cs 文件的 RegisterTypes 方法中添加一条注入规则
1 | public static void RegisterTypes(IUnityContainer container) |