Managed Extensibility Framework 或是简称MEF. 从字面意思来看 MEF是一个用来扩展.NET 应用的框架. MEF做为.NET 4的一部分同时也支持Silverlight 4版本.开发人员能够利用该框架在不对当前代码产生影响的状况下对应用程序加以扩展. 扩展方向既能够在应用程序内重用扩展.也能够在应用程序间重用扩展.这样使用MEF动态编译的.NET 应用程序转换成为一系列的动态组合.有助于对组件实现最大化的重用.而MEF不只使应用程序具有组件化的特性.同时增强了应用程序自身的可扩展性.sql
well.在使用MEF以前假设一个场景.在.NET 4.0中经过dynamic实现轻量级的AOP[Aspect Oriented Programming]组件.若是这些组件数量偏大. 因组件的宿主程序自身没有感知发现组件的能力.因此一般的作法是经过一个XML配置文件来显示注册可用组件.在宿主程序须要接入扩展点提供接口.目的实现应用程序与宿主程序的分离造成结构关系以下:编程
而造成并创建组件与宿主程序的关系的一方面落在一个不管是最终用户仍是开发人员都要加以维护的XML文件之上. 另一方面组件开发必须依赖于包含他们实现的接口的程序集.这样依赖照成一个组件很难在多个应用程序加以复用的问题.安全
介绍MEF在实际项目中使用方法.架构
Managed Extensibility Framewrok 目前第二版本.MEF 2 Privew 5.能够在http://mef.codeplex.com/下载.其实MEF框架实际上微软官方发布的第四个扩展新框架.虽然官方一直声称是第一个. 除了像第三方的Spring.NET这样的框架.微软本身最少还开发其余三个.NET 运行时的扩展框架:app
.NET Runtime扩展框架:框架 [1]Unity 各位很熟悉的微软的企业库Unity Application Block 1.ide [2]CLA插件模型 也就是MEF 以前早期.NET 版本引入的Managed Add-in Framework [MAF]是应用程序可以实现隔离.并管理扩展框架.工具 [3]Composite Application Libray 主要在Silverlight和WPF中使用.熟悉的Prism.组件化 |
其实这里谈到MEF.不得提到早期的Managed Add-In FrameWork[简称Add-in模型].Add-n模型能够解决版本依赖,隔离和故障恢复.MEF第一个版本核心主要解决组件的搜索和组合问题.将组件静态依赖中解放出来.并提供了将不一样编程模型组合在一块儿可能.测试
虽然MEF能够构建在Add-in模型之上能够在安全隔离和版本的稳定性获得很多好处.但从官方介绍来看MEF并无构建在Add-in模型之上.而从头开始编写,并无考虑已有的技术.固然从使用者角度来Add-in出现的时机和使用难易度有很大关系.Add-in模型对于大多数开发人员来讲并很差用.太过复杂.缺少必要的代码生工具.原版本官方也放弃了维护.终究想.NET 3.5中Linq To SQl同样慢慢消失.而在.NET 4推出第二版的MEF 2.
MEF实现原理:
从最简单一个体现实例来讲MEF主要包含三个核心部分:
MEF 核心部分: A:Import[输入] B: Exprot[输出] C: Compose[组件关系组合] |
以这个实例为原型.每一个组件Part能够提供一个或多个对外的Export.而且一般依赖于一个或多个外部宿主程序提供的服务或Import.而Catalog类的则是用于发现指定范围内的扩展关系.CompositionContainer对象则是全部Part组合容器.主要用于协调创建和管理Part组件与宿主程序之间的依赖关系.该容器包含全部可能的组件并执行组合操做.
以下则采用简单实例方式来演示MEF使用.MEF做为.NET 4一个重要部分.开发环境须要.NET 4版本.这里首先采用控制台项目.首先经过MEF的方式来显示当前APPlication 版本信息.
建立Console Application 命名:MEFConsoleDemo.添加MEF所在System.Conpontent.Composition程序集引用.
添加引用:
- using System.ComponentModel;
- using System.ComponentModel.Composition;
- using System.ComponentModel.Composition.Hosting;
如今实如今应用程序版本发生变化是输出版本号信息.做为一个变化扩展点,.采用MEF方式则须要提供两个部分.一个用于提供版本号信息.另一个则用来使用信息.首先定义输出信息.采用关键字Export 定义类ClientRealseVersion实现接口IClientVersion:
- public interface IClientVersion
- {
- string ClientVersion { get; set; }
- }
这里采用Export来标识ClientRealseVersion类ClientVersion属性输出版本信息指定契约名称为接口IClientVersion类型. 并向MEF公开.这时还须要定义一个对应字符串来接受使用版本信息.并用Import标识.该特性将某个对象声明为一个导入;也就是说,在组合对象时将由组合引擎对它进行填充,每一个导入都有一个协定,用于肯定将与之匹配的导出。 协定能够是显式指定的字符串,也能够由 MEF 从给定的类型(在此例中为 IClientVersion 接口)自动生成:
- public class ClientApp
- {
- [Import(typeof(ExportFolder.IClientVersion))]
- public string ClientVersion { get; set; }
- }
当运行时.MEF 会自动检测全部被标识过Import过的属性.而后经过搜索经过Export标识而被导出的类型列表.若是被导出的类型和被标识的属性类型相匹配.则会建立该类型的一个实例.并将其赋给这个属性.
如今又了输入和输出.在主程序中调用时则经过CompositionContainer容器进行组合关联.并添加全部组件到该容器中.在调用Compose()方法关联组合 :
- CompositionContainer mefContainer = new CompositionContainer();
- CompositionBatch mefBatch = new CompositionBatch();
- ClientApp currentClientApp = new ClientApp();
- mefBatch.AddPart(new MEFConsoleDemo.ExportFolder.ClientRealseVersion());
- mefBatch.AddPart(currentClientApp);
- mefContainer.Compose(mefBatch);
- //Console Client App Version Infor
- Console.WriteLine(currentClientApp.ClientVersion);
- mefContainer.Dispose();
运行输出:
当调用Clientapp实例输出当前版本信息时.这里采用CompositionBatch对象.
其实如上演示就是一个简单的依赖注入.着不由让我有些困惑,这和Unity这样的IOC框架具体有什么区别…?
其实虽然可以解决DI的问题.但MEF的核心目的并不在于此.MEF很容易被误用.开发者常常把它做为一个通用的依赖注入框架或控制反转的容器.这些角色并非MEF自己真正目的.IOC模式只是很好解决MEF自身的问题.但MEF真正关注是应用程序的扩展性.而和Unity不一样在于.前者更注重组合.后则强调依赖注入. 而和早期的MAF版本相比.MEF则关注使用简单的方式来支持具备灵活性的可扩展支持.而MAF则注重应用程序的物理的隔离.多版本的支持和安全平台架构.
如上采用CompositionBatch对象处理.固然常见写法使用采用AggregateCatalog对象制定MEF查找程序集范围.获取全部组件的定义.经过CompositionContainer对象建立组合容器.添加组件. 经过ComposePart()方法创建组合关系 写法以下:
- //采用AggregateCatalog 组合关系
- AggregateCatalog mefCatalog = new AggregateCatalog();
- mefCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
- CompositionContainer mefContrainer = new CompositionContainer(mefCatalog);
- ClientVersionInfo currentClientVersion = new ClientVersionInfo();
- mefContrainer.ComposeParts(currentClientVersion);
- Console.WriteLine(currentClientVersion.CurrentVersion);
well.这里小结一下这里面一些MEF特性概念.[我的理解]:
MEF概念: Export[数据导出]:通常针对的是须要向宿主程序提供的组件模块或服务.它是组件向容器中其余组件提供一个值 功能或是服务. Import[数据导入]:Import通常定义在宿主程序中.也就是上面提到扩展点.是组件 服务等接入宿主程序的窗口.MEF中支持的导入类型包含:动态导入.延迟导入.必须导入和可选导入.四种 Catalog[容器目录]:为了发现可用于组合容器的组件.Catalog对象主要用来发现可用组件.MEF 2中从提供的类型,程序集或磁盘路径建立Catalog. Compose[组件组合]:组合是MEF操做的核心.即实现导入和导出匹配的过程.MEF首先将组件实例化.而后执行导入和导出程序之间相匹配. Contract[契约/协定]:准确的翻译是契约.是Export和Import一种约定/.MEF执行组合过程当中只有Contract相匹配的Import和Export才能组合. |
关于协定类型Contract.用处.假定如今修改如上需求.在如今组件基础在作一次扩展.程序运行时同时显示最后一个测试实例运行时间日期/.添加新的接口
- [InheritedExport(typeof(IClientVersion))]
- public interface IClientVersion
- {
- string ClientVersion { get; set; }
- }
这里在接口标识InherItedEXport.指使MEF任何实现该接口的类都会自动导出.实现接口数据导出:
- public class LastTestSampleData:IClientVersion
- {
- public string ClientVersion
- {
- get { return "Last Test Sample Data:" + DateTime.Now.ToLongDateString(); }
- set { }
- }
- }
宿主程序中实现组件的接入接口:
- public class ClientVersionInfo
- {
- [ImportMany]
- public IEnumerable<Lazy<ExportFolder.IClientVersion>> ChangeDataCol { set; get; }
- public string ConsoleChangeData()
- {
- string inputStr = string.Empty;
- if (this.ChangeDataCol != null && this.ChangeDataCol.Count() > 0)
- {
- foreach (ExportFolder.IClientVersion currentVersioninfor in this.ChangeDataCol)
- {
- inputStr += currentVersioninfor.ClientVersion+"\r\n";
- }
- }
- return inputStr;
- }
- }
为了容许访问元数据,MEF 使用 .NET Framework 4 的一个新 API,即 System.Lazy<T>。使用该 API 可延迟实例的实例化,直至访问 Lazy 的 Value 属性。MEF 使用 Lazy<T,TMetadata> 进一步扩展 Lazy<T>,以容许在不实例化基础导出的状况下访问导出元数据.组合代码不变 执行 在输出时调用ConsoleChangeDAta()方法:
当需求发生更改时.组件可以不修改原来宿主程序代码状况下 快速加以扩展. 这样以绝对隔离的方式大大简化原来组件化须要维护和修改的代价.MEF则以很是简单的方式实现灵活的扩展性支持.
本篇主要演示MEF简单的使用方法.MEF基于组件基元在支持强大组合功能下依然具备很好的灵活性.而基于特性的编程模型.使咱们的宿主Code更加简洁.容许咱们使用”特性声明+类的定义”的方式来定义一个具备组合功能的组件.固然在MEF也将在VS2011版本中适用于Windows 8 Metro的应用程序. 但MEF2版本大多数高级功能可能会被移除.只关注MEF的主要通途-暴露扩展点和加载扩展.
官方并无发布MEF For Windows phone 版本.下篇会介绍MEF For Windows phone应用程序中使用.
参考资料:
Managed Extensibility Framework Codeplex
Introduction to Managed Extensibility Framework
Managed Extensibility FrameWork Overview
接口实现:
- public class ClientRealseVersion:IClientVersion
- {
- [Export(typeof(IClientVersion))]
- public string ClientVersion
- {
- get {return "Current Client Version :1.0.4 !"; }
- set { }
- }
- }