使用Unity 实现ASP.NET Web API 依赖注入
DI/IoC 的设计前面已经讲过好几次了,简单的一段话说明就是:「目标对象与外部相依的方式仅相依于 interface,而相依 interface 的 instance 透过 constructor 或 public property 来让外部可以注入相依实体」。 而 DI framework 也是相当多种,这篇文章就简单介绍怎么在 Web API 项目中,简单快速地 adopt Enterprise Library 中的 Unity 。
先建立一个 ASP.NET MVC project, 选择 Web API ,会看到预设长出来的 Controller ,以 ValuesController 的 Get() 为例,程序代码如下所示:
1.
// GET api/values
2.
public
IEnumerable<
string
> Get()
3.
{
4.
return
new
string
[] {
"value1"
,
"value2"
};
5.
}
假设使用 IoC 的设计方式, ValuesController.Get() 方法是透过 IOrderService 这个接口来取值,那么先新增一个 ValuesController 的 constructor ,参数传入 IOrderService ,如下所示:
01.
public
class
ValuesController : ApiController
02.
{
03.
private
IOrderService _orderService;
04.
public
ValuesController(IOrderService orderService)
05.
{
06.
this
._orderService = orderService;
07.
}
08.
09.
// GET api/values
10.
public
IEnumerable<
string
> Get()
11.
{
12.
//return new string[] { "value1", "value2" };
13.
return
this
._orderService.GetValues();
14.
}
15.
16.
}
17.
18.
public
interface
IOrderService
19.
{
20.
IEnumerable<
string
> GetValues();
21.
}
新增一个实作 IOrderService 的 concrete class, 称为 JoeyOrderService ,程序代码如下所示:
1.
public
class
JoeyOrderService : IOrderService
2.
{
3.
public
IEnumerable<
string
> GetValues()
4.
{
5.
return
new
string
[] {
"joey1"
,
"joey2"
};
6.
}
7.
}
假设需求是希望 ValuesController depend on IOrderService ,在实际上是注入 JoeyOrderService ,在使用时不需要再 new ValuesController(new JoeyOrderService) 这么麻烦,拿到 ValuesController 时相关的相依对象都已经被初始化好了,我们只需要使用 DI framework,注册 IOrderService 使用 JoeyOrderService 即可。
这边使用 Unity 来实作这一段,请在 NuGet 加载 Unity.WebAPI。

除了相关组件参考以外, NuGet 还加入了一支 Bootstrapper.cs ,打开来会看到程序代码如下:
01.
using
System.Web.Http;
02.
using
Microsoft.Practices.Unity;
03.
04.
namespace
MvcUnitySample
05.
{
06.
public
static
class
Bootstrapper
07.
{
08.
public
static
void
Initialise()
09.
{
10.
var container = BuildUnityContainer();
11.
12.
GlobalConfiguration.Configuration.DependencyResolver =
new
Unity.WebApi.UnityDependencyResolver(container);
13.
}
14.
15.
private
static
IUnityContainer BuildUnityContainer()
16.
{
17.
var container =
new
UnityContainer();
18.
19.
// register all your components with the container here
20.
// e.g. container.RegisterType<ITestService, TestService>();
21.
22.
return
container;
23.
}
24.
}
25.
}
从批注可以看到,只需要加入 container.RegisterType<TFrom, TTo>() 即可,这边的例子只需要把 IOrderService 与 JoeyOrderService 注册在一起即可,如下所示:
01.
public
static
class
Bootstrapper
02.
{
03.
public
static
void
Initialise()
04.
{
05.
var container = BuildUnityContainer();
06.
07.
GlobalConfiguration.Configuration.DependencyResolver =
new
Unity.WebApi.UnityDependencyResolver(container);
08.
}
09.
10.
private
static
IUnityContainer BuildUnityContainer()
11.
{
12.
var container =
new
UnityContainer();
13.
14.
container.RegisterType<IOrderService, JoeyOrderService>();
15.
return
container;
16.
}
17.
}
接着打开 Global.asax.cs ,在 Application_Start() 的时候,呼叫 Bootstrapper.Initialise() 即可,如下所示:
01.
public
class
WebApiApplication : System.Web.HttpApplication
02.
{
03.
protected
void
Application_Start()
04.
{
05.
Bootstrapper.Initialise();
06.
AreaRegistration.RegisterAllAreas();
07.
08.
WebApiConfig.Register(GlobalConfiguration.Configuration);
09.
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
10.
RouteConfig.RegisterRoutes(RouteTable.Routes);
11.
BundleConfig.RegisterBundles(BundleTable.Bundles);
12.
}
13.
}
这样就大功告成了。我们只做了几件事:
- 把相依的 interface 拉到 constructor or public property ,供外部注入
- 透过 NuGet 加载 Unity.WebAPI
- 在 Bootstrapper 的 BuildUnityContainer() 中注册 interface 对应的 instance type
- 在 Global.asax 中,呼叫 Bootstrapper 的初始化。
来看一下实际的结果,如下图所示:

- 这件事不属于 context 端负责,因为 context 端应该只负责用,而不负责生成目标对象。
- 当对象分割较为独立时,使用一个对象,其相关相依接口可能不少,而对应该接口的实体,也可能还有相依其它接口。因此可能得一大串的 new 之后,才能正常使用一个相依对象。透过 DI framework 的 auto-wiring,会在生成对象的同时,检查 constructor 所使用到的对象,或是标记需要注入的 public property,来产生对应的相依对象自动注入。这样可以节省相当多生成对象的动作。
01.
public
class
ValuesController : ApiController
02.
{
03.
//private IOrderService OrderService;
04.
//public ValuesController(IOrderService orderService)
05.
//{
06.
// this._orderService = orderService;
07.
//}
08.
09.
[Microsoft.Practices.Unity.Dependency]
10.
public
IOrderService OrderService {
get
;
set
; }
11.
12.
// GET api/values
13.
public
IEnumerable<
string
> Get()
14.
{
15.
//return new string[] { "value1", "value2" };
16.
return
this
.OrderService.GetValues();
17.
}
18.
}
这样子效果是一样的。 现在 NuGet 已经相当方便了,如果已经有使用 IoC 的设计,就强烈建议使用方便的 DI framework 来解决生成对象跟相依对象的问题。
原文地址:http://www.it165.net/pro/html/201310/7485.html