本文講述關於用Managed Extensibility Framework (MEF) 的方法來實現IoC和Asp.net的整合。很多人不知道,這個MEF貌似是個大架構,其實已經內建在.NET Framework 4.0裡面了,只需要添加引用System.ComponentModel.Composition即可。MEF在Microsoft的人看來不是一個IoC/DI的工具,而是一個提供輕量級的、可擴充的、類似外掛程式式的系統架構的、且無需配置的(Attribute Based)架構。雖然微軟的人極力否認MEF是一個IoC/DI的工具,但事實是它的確可以實現Ioc/DI。而且相對於Spring.net這樣的架構來說,它的優勢就是首先它是.NET Framework內建的,你無需添加第三方的引用,擔心第三方組件的更新等問題;其次它是免配置的,對Spring.net這樣的龐然大物來說免配置很有誘惑力。對Unity來說,它的優勢是一樣的,.NET Framework內建,無需配置,無需hard code的聲明。當然更無需直接引用了,這是所有IoC都做到的。MEF還可以通過metadata自動探索Parts而無需擷取parts的assembly/dll。有關MEF的完整的介紹,請移步MSDN:http://msdn.microsoft.com/en-us/library/dd460648.aspx。
這裡給出一個例子,用在Asp.net MVC3裡面,主要情境是在Controller裡通過介面擷取中介層的執行個體,本文主要是示範MEF在Asp.net MVC3中的應用,其它豐富的應用完全可以發揮你自己的想象力。本文源碼下載在這裡。
整個項目的結構如:
在Controller中需要調用IBookService與IPhoneService:
Controller的代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Service.Interface;
namespace Mvc3_MEF_WebApplication.Controllers
{
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller
{
private readonly IBookService _bookService;
private readonly IPhoneService _phoneService;
[ImportingConstructor]
public HomeController(IBookService bookService, IPhoneService phoneService)
{
_bookService = bookService;
_phoneService = phoneService;
}
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!" +
_bookService.GetAllBooks().ToList()[0].Name +
_phoneService.GetAllPhones().ToList()[0].SerialNumber;
return View();
}
public ActionResult About()
{
return View();
}
}
}
BookService的實現:
BookService的代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using Data.Model;
using Service.Interface;
namespace Service.Implement
{
[Export(typeof(IBookService))]
public class DefaultBookService : IBookService
{
public IEnumerable<Book> GetAllBooks()
{
yield return new Book
{
ID = 1,
Name = "a"
};
yield return new Book
{
ID = 2,
Name = "b"
};
}
}
}
在Global.asax.cs裡面需要設定MEF dependency resolver,自動載入所有bin目錄下面的assembly/dll
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//Set MEF dependency resolver
var catalog =
new DirectoryCatalog(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
var container =
new CompositionContainer(catalog);
container.ComposeParts(this);
var mefDependencySolver = new MefDependencySolver(container);
DependencyResolver.SetResolver(mefDependencySolver);
}
MefDependencySolver的代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Web.Mvc;
namespace Mvc3_MEF_WebApplication.Extension
{
public class MefDependencySolver : IDependencyResolver
{
public MefDependencySolver(CompositionContainer compositionContainer)
{
_compositionContainer = compositionContainer;
}
private readonly CompositionContainer _compositionContainer;
public object GetService(Type serviceType)
{
var name = AttributedModelServices.GetContractName(serviceType);
return _compositionContainer.GetExportedValueOrDefault<object>(name);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _compositionContainer
.GetExportedValues<object>(serviceType.FullName);
}
}
}
最後系統啟動並執行結果如:
小結
總結一下所有的步驟:
1. 項目添加引用: System.ComponentModel.Composition
2. 為需要的Parts標註[Import] [Export] 等attribute
3. 建立一個CompositionContainer執行個體
4. 實現IDependencyResolver介面
5. 註冊到DependencyResolver
更多資源
本文的實現很簡單,拋磚引玉,更多內容可以參考以下資料:
1. MSDN: MEF Overview
2. MEF Programming Guide
3. MSDN Magazine: Building Composable Apps in .NET 4 with the Managed Extensibility Framework
另外StackOverflow還有很多針對MEF進階議題(importMany集合匯入, lazy惰性匯入, composition/AllowMultiple複合, metaData, lifetime等進階議題。)的討論,可以參考下。
此外,用MEF為Asp.net mvc 建立一個ControllerFactory可以參考這篇文章。 (用Unity的可以參考Artech的這篇文章。)
用MEF實現Asp.net MVC的模組化開發有興趣可以參考 這篇文章。
本文源碼下載在這裡。