預設範本所產生的程式碼會產生與NorthwindEntities資料實體(new NorthwindEntities())產生依賴關係。每個API Controller都還要處理db.Products ,這造成API Controller的職責不夠單一,這在裡我們就先採用Repository Pattern 1 來分離資料庫處理程式碼與API Controller的相依關係。
01
public
class
ProductsController : ApiController
05
private
IProductRepository _product;
15
/// <param name="r">實作的介面</param>
16
public
Products2Controller(IProductRepository r)
22
public
IEnumerable<Product> GetProducts()
24
return
_product.GetAll();
最終希望可以把Controller裡特定實體相依性給移除。然後透過IoC/DI套件的幫忙,讓我們可以透過Repository介面的幫助下去自動產生實體。
以下會介面二套IoC/DI套件,並討論在MVC與Web API的實作方式。
Web API DI Framework - Autofac
Install-Package autofac.WebApi
Autofac算是老牌的IoC與DI實作套件,它對於Web API/Web API 2 或是MVC 2/MVC 3/MVC 4/MVC 5 都有快速與良好的支援。這是讓我選擇使用它的一個主因。
以下是《ASP.NET MVC 4網站開發美學 》第七章 Page 7-46頁的程式碼:
02
ContainerBuilder builder =
new
ContainerBuilder();
05
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
06
.Where(t => t.Name.EndsWith(
"Repository"
)).AsImplementedInterfaces();
13
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
14
.Where(t => !t.IsAbstract &
typeof
(ApiController)
16
.InstancePerMatchingLifetimeScope(AutofacWebApiDependencyResolver.ApiRequestTag);
19
IContainer container = builder.Build();
21
AutofacWebApiDependencyResolver resolver =
new
AutofacWebApiDependencyResolver(container);
23
config.DependencyResolver = resolver;
以上是Autofac在Web API剛推出時所撰寫的程式碼。不過後來Autofac 3.x.x針對Web API方面有進行重構,以下是新版程式碼:
02
using
Autofac.Integration.WebApi;
05
var builder =
new
ContainerBuilder();
07
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
09
builder.Register(c =>
new
ProductRepository()).As<IProductRepository>().InstancePerApiRequest();
11
var container = builder.Build();
13
var resolver =
new
AutofacWebApiDependencyResolver(container);
15
GlobalConfiguration.Configuration.DependencyResolver = resolver;
透過builder.Register()
方法非常容易就註冊實體類別與介面的對應關係。現在API Controller已經可以在沒有任何實體,透過Autofac套件的幫忙正常運作。
2 以上兩段程式碼都能正常運作。
Web API 2 DI Framework - Autofac
Install-Package autofac.WebApi2
程式碼完全相容,不過WebApiConfig.cs註冊有些差異:
ASP.NET Web API
1
WebApiConfig.SetAutofacDI(GlobalConfiguration.Configuration);
ASP.NET Web API 2
1
GlobalConfiguration.Configure(WebApiConfig.SetAutofacDI);
Web API DI Framework - Unity Application Block
Install-Package unity.WebAPI
以下是《ASP.NET MVC 4網站開發美學 》第七章 Page 7-48頁的程式碼:
01
public
static
void
Register(HttpConfiguration config)
04
var container = BuildUnityContainer();
05
GlobalConfiguration.Configuration.DependencyResolver =
new
Unity.WebApi.UnityDependencyResolver(container);
08
private
static
IUnityContainer BuildUnityContainer()
10
var container =
new
UnityContainer();
14
container.RegisterType<IProductRepository, ProductRepository>();
讀者如果實作做過一次,就會發現程式碼其實只有一行,將實體與介面的對應關係透過container.RegisterType()方法一一註冊進去即可。
3 可另外參考91哥的[ASP.NET Web API]3 分鐘搞定 DI framework–Unity Application Block
Web API 2 DI Framework - Unity Application Bloc
unity.WebAPI 撰文期間(2013/10/29)未支援 ASP.NET Web API 2。
ASP.NET MVC 4 DI Framework - Autofac
MVC專案先透過Repository Pattern來整理Controller裡的資料庫存取程式碼。然後使用NuGet安裝必需要的元件:
Install-Package autofac.Mvc4
autofac.Mvc4會自動加入相依的組件。我們盡量保持 Global.asax 檔的乾淨,新增一個AutofacConfig.cs來整理我們的DI Framework程式碼:
02
using
Autofac.Integration.Mvc;
04
public
static
class
AutofacConfig
06
public
static
void
Register()
09
var builder =
new
ContainerBuilder();
11
builder.RegisterControllers(Assembly.GetExecutingAssembly());
13
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
14
.Where(t => t.Name.EndsWith(
"Repository"
))
15
.AsImplementedInterfaces();
21
var container = builder.Build();
23
DependencyResolver.SetResolver(
new
AutofacDependencyResolver(container));
MVC 4的Autofac註冊程式碼和Web API其實差不多,透過builder.RegisterAssemblyTypes()
方法去搜尋尾綴(Repository | Service)的檔案名稱,並進行註冊動作。
搜尋尾綴的方法,在大部分的專案情況下都可以符合需求了。不過還有一種註冊方法也不錯用,就是透過搜尋NameSpace的方式來搜尋與註冊。
圖片來源:http://weblogs.asp.net/bsimser/
如果你的Models很複雜,可能會利用目錄來整理相關類別與實作。那麼我們只需要透過NameSpace來進行搜尋與註冊的動作即可:
01
public
static
void
Register()
04
var builder =
new
ContainerBuilder();
07
builder.RegisterControllers(Assembly.GetExecutingAssembly());
11
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
12
.Where(t => t.Namespace.EndsWith(
".Repositories"
))
13
.AsImplementedInterfaces();
16
var container = builder.Build();
19
DependencyResolver.SetResolver(
new
AutofacDependencyResolver(container));
透過Lambda運算式「t.Namespace.EndsWith(".Repositories")
」找出此NameSpace下所有介面與實作並進行註冊動作。
ASP.NET MVC 5 DI Framework - Autofac
Install-Package autofac.Mvc5
ASP.NET MVC 5必須用使用autofac.Mvc5套件版本。
實作程式碼與ASP.NET MVC 4一模一樣。互相通用。
ASP.NET MVC 4 DI Framework - Unity Application Block
Install-Package unity.Mvc4
這邊讓我偷懶一下,我直修改安裝好的Bootstrapper.cs:
01
public
static
IUnityContainer Initialise()
03
var container = BuildUnityContainer();
04
DependencyResolver.SetResolver(
new
UnityDependencyResolver(container));
08
private
static
IUnityContainer BuildUnityContainer()
10
var container =
new
UnityContainer();
14
container.RegisterType<IProductRepository, ProductRepository>();
15
RegisterTypes(container);
19
public
static
void
RegisterTypes(IUnityContainer container)
我是習慣將組態檔整理至App_Start目錄,這樣比較統一。新增介面與實體的對應關係只有一行程式碼:「container.RegisterType<IProductRepository, ProductRepository>();
」,使用方式和Web API的Unity.WebApi一樣簡便。
ASP.NET MVC 5 DI Framework - Unity Application Block
Install-Package unity.Mvc4
目前在ASP.NET MVC 5使用與設置並無差異,測試unity.Mvc4組件在MVC 5專案可以正常作運。
IoC / DI套件執行效率
目前世面上的IoC / DI套件很多,有位Daniel 比較受歡迎的IoC / DI套件的執行效率。如果看到文末的Updates ,就知道這篇文章有非常不錯的參考價值,因為套件會不算改版升級,作者從2011年一直到2013年都有在更新。
所以請問一下目前有針對web api2支援的嗎??我目前找了一堆,都顯示這種錯誤,無法載入檔案或組件 'System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' 或其相依性的其中之一。 找到的組件資訊清單定義與組件參考不符。 (發生例外狀況於 HRESULT: 0x80131040)
回覆刪除Version=4.0.0.0,應該是 Web API 不是 Web API 2 哦!
刪除不好意思,我目前已經更新到最新版的web api2.1了,目前使用您文內介紹的Autofac,不過在您指到的這段code。
刪除ASP.NET Web API 2
1 GlobalConfiguration.Configure(WebApiConfig.SetAutofacDI);
SetAutofacDI解析不到,請問一下除了安裝autofac.WebApi2,還需要安裝什麼,或者是什麼原因呢??請指導小弟一下好嗎??非常的感謝。
WebApiConfig.SetAutofacDI ... 要自己寫呀。
回覆刪除怎麼寫,本 Blog 旁邊有一本書,沒記錯的話,書裡面有教 :P
你好,你說的書是指「asp.net mvc5網站開發美學」這本嗎?
刪除這本我手邊有一本,但是找不到相關的介紹耶,還是只在mvc4的那本才有介紹?
謝謝
MVC 4
刪除你好,我是你的asp.net mvc 4 網站開發美學的讀者,我在實作web api的章節時,做到相依注入的部分在使用autofac這個套件時發生問題
回覆刪除照書上的步驟做下來,在瀏覽器輸入http://http://localhost:62284/api/products/時會出現
An error has occurred.
No constructors on type 'MvcApplication2.Controllers.APIs.ProductsController' can be found with the constructor finder 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'.
Autofac.Core.DependencyResolutionException
於 Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters) 於 Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters) 於 Autofac.Core.Resolving.InstanceLookup.Execute() 於 Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters) 於 Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters) 於 Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters) 於 Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters) 於 Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) 於 Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters) 於 Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType, IEnumerable`1 parameters) 於 Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType) 於 Autofac.Integration.WebApi.AutofacWebApiDependencyScope.GetService(Type serviceType) 於 System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) 於 System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
這個錯誤訊息~~原本以為我下載的版本較新(autofac.webapi 3.1.0版)將SetAutofacDI這個方法的內容改成你這裡寫的
新版本,結果還是會出現這個錯誤訊息,可否請你解答一下??謝謝