[ASP.NET Web API]3 分鐘搞定 DI framework–Unity Application Block
前言
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() 為例,程式碼如下所示:
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
假設使用 IoC 的設計方式, ValuesController.Get() 方法是透過 IOrderService 這個介面來取值,那麼先新增一個 ValuesController 的 constructor ,參數傳入 IOrderService ,如下所示:
public class ValuesController : ApiController
{
private IOrderService _orderService;
public ValuesController(IOrderService orderService)
{
this._orderService = orderService;
}
// GET api/values
public IEnumerable<string> Get()
{
//return new string[] { "value1", "value2" };
return this._orderService.GetValues();
}
}
public interface IOrderService
{
IEnumerable<string> GetValues();
}
新增一個實作 IOrderService 的 concrete class, 稱為 JoeyOrderService ,程式碼如下所示:
public class JoeyOrderService : IOrderService
{
public IEnumerable<string> GetValues()
{
return new string[] { "joey1", "joey2" };
}
}
假設需求是希望 ValuesController depend on IOrderService ,在實際上是注入 JoeyOrderService ,在使用時不需要再 new ValuesController(new JoeyOrderService) 這麼麻煩,拿到 ValuesController 時相關的相依物件都已經被初始化好了,我們只需要使用 DI framework,註冊 IOrderService 使用 JoeyOrderService 即可。
這邊使用 Unity 來實作這一段,請在 NuGet 加載 Unity.WebAPI
。
除了相關組件參考以外, NuGet 還加入了一支 Bootstrapper.cs ,打開來會看到程式碼如下:
using System.Web.Http;
using Microsoft.Practices.Unity;
namespace MvcUnitySample
{
public static class Bootstrapper
{
public static void Initialise()
{
var container = BuildUnityContainer();
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
// register all your components with the container here
// e.g. container.RegisterType<ITestService, TestService>();
return container;
}
}
}
從註解可以看到,只需要加入 container.RegisterType<TFrom, TTo>() 即可,這邊的例子只需要把 IOrderService 與 JoeyOrderService 註冊在一起即可,如下所示:
public static class Bootstrapper
{
public static void Initialise()
{
var container = BuildUnityContainer();
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
container.RegisterType<IOrderService, JoeyOrderService>();
return container;
}
}
接著打開 Global.asax.cs ,在 Application_Start() 的時候,呼叫 Bootstrapper.Initialise() 即可,如下所示:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
Bootstrapper.Initialise();
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
這樣就大功告成了。我們只做了幾件事:
- 把相依的 interface 拉到 constructor or public property ,供外部注入
- 透過 NuGet 加載 Unity.WebAPI
- 在 Bootstrapper 的 BuildUnityContainer() 中註冊 interface 對應的 instance type
- 在 Global.asax 中,呼叫 Bootstrapper 的初始化。
來看一下實際的結果,如下圖所示:
結論
為什麼還需要透過 DI framework 來決定相依物件呢?原因如下:
- 這件事不屬於 context 端負責,因為 context 端應該只負責用,而不負責生成目標物件。
- 當物件分割較為獨立時,使用一個物件,其相關相依介面可能不少,而對應該介面的實體,也可能還有相依其他介面。因此可能得一大串的 new 之後,才能正常使用一個相依物件。透過 DI framework 的 auto-wiring,會在生成物件的同時,檢查 constructor 所使用到的物件,或是標記需要注入的 public property,來產生對應的相依物件自動注入。這樣可以節省相當多生成物件的動作。
上面的例子是透過 constructor, 如果希望透過 property 來做注入,也相當簡單,只需要在 property 上標示:[Microsoft.Practices.Unity.Dependency]
這個 Attribute 即可,程式碼如下所示:
public class ValuesController : ApiController
{
//private IOrderService OrderService;
//public ValuesController(IOrderService orderService)
//{
// this._orderService = orderService;
//}
[Microsoft.Practices.Unity.Dependency]
public IOrderService OrderService { get; set; }
// GET api/values
public IEnumerable<string> Get()
{
//return new string[] { "value1", "value2" };
return this.OrderService.GetValues();
}
}
這樣子效果是一樣的。
現在 NuGet 已經相當方便了,如果已經有使用 IoC 的設計,就強烈建議使用方便的 DI framework 來解決生成物件跟相依物件的問題。
Reference
- IoC 設計的介紹:[30天快速上手TDD][Day 5]如何隔離相依性 - 基本的可測試性
- 董大偉老師介紹 Unity Application Block: MS Enterprise Library 6.0(一) - Unity Application Block
- 小風介紹在 Web API 使用 Autofac: 使用Asp.Net MVC打造Web Api (4) - 套用DI Framework – Autofac
- 黑暗執行緒介紹 Autofac: Autofac 筆記
blog 與課程更新內容,請前往新站位置:http://tdd.best/