使用MEF实现IOC
〇、目录
一、前言
(一)什么是IOC
(二)什么是MEF
(三)为什么选择MEF
二、准备工作
五、总结
六、源码下载
一、前言
(一)什么是IOC
什么是IOC?基本含义是:当某个角色(调用者)需要另一个角色(被调用者)的协助时,在传统程序设计过程中,通常由调用者来创建被调用者的实例。但在加入IOC组件后,创建被调用者实例的工作不再由调用者来完成,而是将由IOC组件来自动完成,然后注入调用者。
网上已经非常非常多的介绍资料了,这里就不赘述了,这里给出几篇参考:
(二)什么是MEF
Managed Extensibility Framework 或 MEF 是一个用于创建可扩展的轻型应用程序的库。 应用程序开发人员可利用该库发现并使用扩展,而无需进行配置。 扩展开发人员还可以利用该库轻松地封装代码,避免生成脆弱的硬依赖项。 通过 MEF,不仅可以在应用程序内重用扩展,还可以在应用程序之间重用扩展。
下面这篇文章对MEF的介绍非常到位:
(三)为什么选择MEF
已经有很多很好的诸如Unity,Autofac,Ninject,Spring.Net等等IOC组件了,为什么我还要推荐使用MEF呢,下面是我推荐的理由:
- .net4.0自带,非第三方组件
- 0配置,没有像其他IOC组件那样需要复杂的外部配置支持
- 使用非常简单的方式来提供具有强大灵活性的可扩展性支持
二、准备工作
本文案例很简单,一个接口
这个接口的实例类
我们要做的工作就是要通过MEF来解除业务使用者对具体业务实现的依赖关系。
要使用MEF,需添加 System.ComponentModel.Composition 类库的引用
与其他通过外部配置文件来完成接口与实现类的对应关系的IOC组件不同,MEF是通过 ExportAttribute 特性来完成的(如有多个实现,可加入别名来区分),如下图所示:
三、MEF在桌面程序中的使用
在桌面程序中,需要完成两个部分的目录匹配,一个是dll中的匹配,另一个为exe程序集中的匹配,分别使用到DirectoryCatalog与AssemblyCatalog两个目录类。而两个目录类需加入到 AggregateCatalog 目录类中,才能参与组合容器CompositionContainer的初始化。
在调用方,只需要使用 ImportAttribute 特性来注入即可获得被调用者的实例
由于这里调用方法是静态方法,手动从组合容器中获取 调用者HomeBusiness类的实例
输出,得到了业务实现类 HomeService返回的结果
四、MEF在MVC中的使用
在MVC的项目中,IOC组件是通过 DependencyResolver类中的 SetResolver(IDependencyResolver resolver) 方法来向MVC提供注册点的,所以我们只需要实现一个 IDependencyResolver 接口的MEF实现类,即可完成MEF在MVC中的注册工作。
另外考虑到Web应用程序的无状态性,即每次访问都是独立进行的,所以IOC组件产生的对象实例也必须唯一,否则不同用户的操作就可能串线,产生相互干扰。在这里,我们使用HttpContext.Current.Items集合来保存 组合容器CompositionContainer的实例,以使每个用户的数据保持独立,并且同一用户的同一个Http请求中使用同一对象实例。
MefDependencySolver实现代码如下:
1 namespace Liuliu.Demo.Site.Web.Helper.Ioc 2 { 3 public class MefDependencySolver : IDependencyResolver 4 { 5 private readonly ComposablePartCatalog _catalog; 6 private readonly string _httpContextKey = Guid.NewGuid().ToString(); 7 8 public MefDependencySolver(ComposablePartCatalog catalog) 9 { 10 _catalog = catalog; 11 } 12 13 public CompositionContainer Container 14 { 15 get 16 { 17 if (!HttpContext.Current.Items.Contains(_httpContextKey)) 18 { 19 HttpContext.Current.Items.Add(_httpContextKey, new CompositionContainer(_catalog)); 20 } 21 CompositionContainer container = (CompositionContainer)HttpContext.Current.Items[_httpContextKey]; 22 HttpContext.Current.Application["Container"] = container; 23 return container; 24 } 25 } 26 27 #region IDependencyResolver Members 28 29 public object GetService(Type serviceType) 30 { 31 string contractName = AttributedModelServices.GetContractName(serviceType); 32 return Container.GetExportedValueOrDefault<object>(contractName); 33 } 34 35 public IEnumerable<object> GetServices(Type serviceType) 36 { 37 return Container.GetExportedValues<object>(serviceType.FullName); 38 } 39 40 #endregion 41 } 42 }
在Global.asax.cs的Application_Start方法中初始化MEF容器,由于Web应用程序中只需要在DLL中查找匹配,所以只使用DirectoryCatalog即可。
作为调用者,Controller中同样需要添加被调用者的注入信息
1 namespace Liuliu.Demo.Site.Web.Controllers 2 { 3 [Export] 4 public class HomeController : Controller 5 { 6 [Import] 7 public IHomeContract HomeContract { get; set; } 8 9 public ActionResult Index() 10 { 11 string data = HomeContract.GetData(); 12 return Content(data); 13 } 14 } 15 }
执行程序,同样输出了HomeService的返回数据:
五、总结
MEF不同于显式注册可用组件的做法,在MEF中组件被视为部件。这些部件主要有“导入”(Import)部件和“导出”(Export)部件,此外MEF提供了一个组合容器(ComposeContainer),容器会发现指定Catalog的导入导出部件,并按Contract(协定)、Metadata(元数据)等对导入和导出部件进行组合。
上述文字已经把MEF中涉及的相关概念指出了,下面具体说明一下:
Export(导出): “Export”也就是我们常说的组件或者模块或者服务,它是部件向容器中的其他部件提供的一个值、功能或服务等;
Import(导入): "Import”,既扩展点,是组件,服务等接入系统的窗口,是部件向要通过可用导出满足的容器提出的要求,MEF 支持若干导入类型,其中包括动态导入、延迟导入、必备导入和可选导入;
Contract(协定):是Export和Import的一种约定,一种协议,只有Contract相匹配的Import和Export部件才能组装成功;
Catalog(目录):为了发现可用于组合容器的部件,组合容器将使用“Catalog”。 目录是一个对象,通过它发现可用部件, MEF 提供了用于从提供的类型、程序集或磁盘路径创建Catalog。
Compose(组合):在MEF中,容器将导入与导出匹配的这一过程我们称之为组合,部件由 MEF 组合,MEF 将部件实例化,然后使导出程序与导入程序相匹配。