代码改变世界

Windows phone 应用开发[6]-Managed Extensibility Framework应用程序扩展

2011-12-27 19:05  chenkai  阅读(2095)  评论(4编辑  收藏  举报

Managed Extensibility Framework 或是简称MEF. 从字面意思来看 MEF是一个用来扩展.NET 应用的框架. MEF作为.NET 4的一部分同时也支持Silverlight 4版本.开发人员可以利用该框架在不对当前代码产生影响的情况下对应用程序加以扩展. 扩展方向既可以在应用程序内重用扩展.也可以在应用程序间重用扩展.这样使用MEF动态编译的.NET 应用程序转换成为一系列的动态组合.有助于对组件实现最大化的重用.而MEF不仅使应用程序具备组件化的特性.同时加强了应用程序自身的可扩展性.

well.在使用MEF之前假设一个场景.在.NET 4.0中通过dynamic实现轻量级的AOP[Aspect Oriented Programming]组件.如果这些组件数量偏大. 因组件的宿主程序自身没有感知发现组件的能力.所以通常的做法是通过一个XML配置文件来显示注册可用组件.在宿主程序需要接入扩展点提供接口.目的实现应用程序与宿主程序的分离形成结构关系如下:

2011-12-27_113557

而形成并建立组件与宿主程序的关系的一方面落在一个无论是最终用户还是开发人员都要加以维护的XML文件之上. 另外一方面组件开发必须依赖于包含他们实现的接口的程序集.这样依赖照成一个组件很难在多个应用程序加以复用的问题.

介绍MEF在实际项目中使用方法.

<1>MEF原理

Managed Extensibility Framewrok 目前第二版本.MEF 2 Privew 5.可以在http://mef.codeplex.com/下载.其实MEF框架实际上微软官方发布的第四个扩展新框架.虽然官方一直声称是第一个. 除了像第三方的Spring.NET这样的框架.微软自己最少还开发其他三个.NET 运行时的扩展框架:

.NET Runtime扩展框架:

[1]Unity 各位很熟悉的微软的企业库Unity Application Block 1.

[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实现原理:

image_2

从最简单一个体现实例来说MEF主要包含三个核心部分:

MEF 核心部分:

A:Import[输入]

B: Exprot[输出]

C: Compose[组件关系组合]

以这个实例为原型.每个组件Part可以提供一个或多个对外的Export.并且通常依赖于一个或多个外部宿主程序提供的服务或Import.而Catalog类的则是用于发现指定范围内的扩展关系.CompositionContainer对象则是所有Part组合容器.主要用于协调建立和管理Part组件与宿主程序之间的依赖关系.该容器包含所有可能的组件并执行组合操作.

<2>MEF使用

如下则采用简单实例方式来演示MEF使用.MEF作为.NET 4一个重要部分.开发环境需要.NET 4版本.这里首先采用控制台项目.首先通过MEF的方式来显示当前APPlication 版本信息.

创建Console Application 命名:MEFConsoleDemo.添加MEF所在System.Conpontent.Composition程序集引用.

2011-12-27_143207

添加引用:

   1:  using System.ComponentModel;
   2:  using System.ComponentModel.Composition;
   3:  using System.ComponentModel.Composition.Hosting;

现在实现在应用程序版本发生变化是输出版本号信息.作为一个变化扩展点,.采用MEF方式则需要提供两个部分.一个用于提供版本号信息.另外一个则用来使用信息.首先定义输出信息.采用关键字Export 定义类ClientRealseVersion实现接口IClientVersion:

   1:     public interface IClientVersion
   2:      {
   3:          string ClientVersion { get; set; }
   4:      }

接口实现:

   1:     public class ClientRealseVersion:IClientVersion
   2:      {
   3:          [Export(typeof(IClientVersion))]
   4:          public string ClientVersion
   5:          {
   6:              get {return "Current Client Version :1.0.4 !"; }
   7:              set { }
   8:          }
   9:      }

这里采用Export来标识ClientRealseVersion类ClientVersion属性输出版本信息指定契约名称为接口IClientVersion类型. 并向MEF公开.这时还需要定义一个对应字符串来接受使用版本信息.并用Import标识.该特性将某个对象声明为一个导入;也就是说,在组合对象时将由组合引擎对它进行填充,每个导入都有一个协定,用于确定将与之匹配的导出。 协定可以是显式指定的字符串,也可以由 MEF 从给定的类型(在此例中为 IClientVersion 接口)自动生成:

   1:      public class ClientApp
   2:      {
   3:          [Import(typeof(ExportFolder.IClientVersion))]
   4:          public string ClientVersion { get; set; }
   5:       }
当运行时.MEF 会自动检测所有被标识过Import过的属性.然后通过搜索通过Export标识而被导出的类型列表.如果被导出的类型和被标识的属性类型相匹配.则会创建该类型的一个实例.并将其赋给这个属性.

现在又了输入和输出.在主程序中调用时则通过CompositionContainer容器进行组合关联.并添加所有组件到该容器中.在调用Compose()方法关联组合 :

   1:              CompositionContainer mefContainer = new CompositionContainer();
   2:              CompositionBatch mefBatch = new CompositionBatch();
   3:   
   4:              ClientApp currentClientApp = new ClientApp();
   5:              mefBatch.AddPart(new MEFConsoleDemo.ExportFolder.ClientRealseVersion());
   6:              mefBatch.AddPart(currentClientApp);
   7:   
   8:              mefContainer.Compose(mefBatch);
   9:   
  10:              //Console Client App Version Infor
  11:              Console.WriteLine(currentClientApp.ClientVersion);
  12:              mefContainer.Dispose();

运行输出:

2011-12-27_145421

当调用Clientapp实例输出当前版本信息时.这里采用CompositionBatch对象.

其实如上演示就是一个简单的依赖注入.着不禁让我有些困惑,这和Unity这样的IOC框架具体有什么区别…?

其实虽然能够解决DI的问题.但MEF的核心目的并不在于此.MEF很容易被误用.开发者经常把它作为一个通用的依赖注入框架或控制反转的容器.这些角色并不是MEF本身真正目的.IOC模式只是很好解决MEF自身的问题.但MEF真正关注是应用程序的扩展性.而和Unity不同在于.前者更注重组合.后则强调依赖注入. 而和早期的MAF版本相比.MEF则关注使用简单的方式来支持具有灵活性的可扩展支持.而MAF则注重应用程序的物理的隔离.多版本的支持和安全平台架构.

如上采用CompositionBatch对象处理.当然常见写法使用采用AggregateCatalog对象制定MEF查找程序集范围.获取所有组件的定义.通过CompositionContainer对象创建组合容器.添加组件. 通过ComposePart()方法建立组合关系 写法如下:

   1:   
   2:             //采用AggregateCatalog 组合关系
   3:              AggregateCatalog mefCatalog = new AggregateCatalog();
   4:              mefCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
   5:   
   6:              CompositionContainer mefContrainer = new CompositionContainer(mefCatalog);
   7:              ClientVersionInfo currentClientVersion = new ClientVersionInfo();
   8:              mefContrainer.ComposeParts(currentClientVersion);
   9:   
  10:              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.用处.假定现在修改如上需求.在现在组件基础在做一次扩展.程序运行时同时显示最后一个测试实例运行时间日期/.添加新的接口

   1:      [InheritedExport(typeof(IClientVersion))]
   2:      public interface IClientVersion
   3:      {
   4:          string ClientVersion { get; set; }
   5:      }

这里在接口标识InherItedEXport.指使MEF任何实现该接口的类都会自动导出.实现接口数据导出:

   1:     public class LastTestSampleData:IClientVersion
   2:      {
   3:          public string ClientVersion
   4:          {
   5:              get { return "Last Test Sample Data:" + DateTime.Now.ToLongDateString();  }
   6:              set { }      
   7:          }
   8:      }

宿主程序中实现组件的接入接口:

   1:  public  class ClientVersionInfo
   2:      {
   3:          [ImportMany]
   4:          public IEnumerable<Lazy<ExportFolder.IClientVersion>> ChangeDataCol { set; get; }
   5:   
   6:          public string ConsoleChangeData()
   7:          {
   8:              string inputStr = string.Empty;
   9:              if (this.ChangeDataCol != null && this.ChangeDataCol.Count() > 0)
  10:              {
  11:                  foreach (ExportFolder.IClientVersion currentVersioninfor in this.ChangeDataCol)
  12:                  {
  13:                      inputStr += currentVersioninfor.ClientVersion+"\r\n";
  14:                  }
  15:              }
  16:              return inputStr;
  17:          }
  18:   
  19:      }

为了允许访问元数据,MEF 使用 .NET Framework 4 的一个新 API,即 System.Lazy<T>。使用该 API 可延迟实例的实例化,直至访问 Lazy 的 Value 属性。MEF 使用 Lazy<T,TMetadata> 进一步扩展 Lazy<T>,以允许在不实例化基础导出的情况下访问导出元数据.组合代码不变 执行 在输出时调用ConsoleChangeDAta()方法:

2011-12-27_184003

当需求发生更改时.组件能够不修改原来宿主程序代码情况下 快速加以扩展. 这样以绝对隔离的方式大大简化原来组件化需要维护和修改的代价.MEF则以非常简单的方式实现灵活的扩展性支持.

<3>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

无觅相关文章插件,快速提升流量