前面一篇介紹了ASP.NET MVC3 和 Unity 結合使用的樣本,Unity 通過 Register 方法或者配置注入執行個體,MEF 則是通過 [Import] [Export] 特性綁定依賴。在 MEF 2.0 中當前 dll 中如果在 *.Parts.* 命名空間下的類型會自動作為依賴源。CompositionProvider.AddPartsAssembly 亦可運行時添加依賴對象,非常靈活。通過中繼資料定義的依賴和通過XML來描述依賴關係是各有利弊,XML方式勢必帶來配置和維護的學習成本(編碼注入是一種寫法,配置又是另一種寫法),而中繼資料定義方式使得關係淩亂分散,侵入性也較強。
最新的MEF 2.0 Preview 5 加入了 System.ComponentModel.Composition.Web.Mvc.CodePlex.dll 簡化了構建一個靈活的,可測試的ASP.NET MVC應用程式,實現:
1)為Controller提供依賴注入
2)通過定義簡單的約定(Import & Export) 識別和配置 MEF Parts(組合)
3)將依賴執行個體的生存周期映射到Request的生存周期上
4) 簡化 ActionFilter 和 ModelBinder 的依賴注入實現
* 目前還沒有提供 nuget 的方式,所以只能下載 dll 手動添加:
1) System.ComponentModel.Composition.CodePlex.dll
2) System.ComponentModel.Composition.Web.Mvc.CodePlex.dll
下面來看看範例程式碼,如何利用MEF實現ASP.NET MVC的IoC
MvcWithMefTest 是 MVC Application,Controller IoC 是目標。GenericRepository 是介面,MvcWithMefTest.Repository 是要注入的依賴。
(GenericRepository 和 Models 的代碼,參看我上篇blog:【ASP.NET】ASP.NET MVC 3 & Unity.MVC3 )
1. MvcWithMefTest.Repository 的 Export
using System;using System.Collections.Generic;using GenericRepository;using MvcWithMefTest.Models;using System.ComponentModel.Composition;using System.Data.Entity;namespace MvcWithMefTest.Repository{ [Export(typeof(IRepository<User>))] public class UserRepository : DbContextRepository<User> { [ImportingConstructor] public UserRepository([Import("Database")]DbContext context) : base(context) { } } public class InjectDbInstance { [Export("Database")] public DbContext Database { get { return new DbEntities(); } } }}
上面的代碼可以看到,DbContext 執行個體通過名稱化的 ImportingConstructor 注入。對於構造方法的執行個體注入,不知道還有沒有更好的辦法?
雖然用 CompositionBatch.AddExportedValue 可以在 Global.asax 裡執行個體化 DbContext 注入,但是拿不到 CompositionContainer 執行個體,沒法完成最後的註冊...
看來有必要再研究下 CompositionProvider 的實現機制。
2. Controller 中的 IRepository Import
public class HomeController : Controller{ [Import] GenericRepository.IRepository<User> _userRepository; public ActionResult Index() { var users = _userRepository.GetAll(); return View(users); }}
3. Global.asax 裡完成 AddPartsAssembly
protected void Application_Start(){ Database.SetInitializer(new MvcWithMefTest.Models.SampleData()); AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); CompositionProvider.AddPartsAssembly(typeof(UserRepository).Assembly);}
這裡 AddPartsAssembly 是通過 typeof(xxx).Assembly 來實現的,完全可以通過 Assembly.LoadFrom / LoadFile 完成實際的物理分離(即 Application 工程不添加 Repository.dll 的引用)