MEF 扩展性管理框架

MEF是做什么的?
在MEF诞生前,已经存在许多依赖注入框架来解决应用的扩展性问题,比如 EJB、CORBA、Eclipse 的 OSGI 实现以及 Java 端的 Spring 等等。在 Microsoft 的平台上,.NET Framework 自身内部包含组件模型和 System.Addin,此外还有不少开源解决方案,包括SharpDevelop 的SODA 体系结构和“控制反转”容器(如Castle Windsor、Structure Map、Spring.Net 以及Unity)。

在Microsoft看来,这些方案有些过于庞杂(比如OSGI),有些则需要开发人员完成许多额外工作(比如Spring),MEF试图秉承这些解决方案的优点,尝试解决刚才所提及的令人头痛的问题。MEF有两个优点:第一,MEF是开源项目,其源代码在Codeplex上可以下载,第二,MEF是第一个随CLR发布的扩展性管理的框架,而且在Visual Studio和Silverlight中被广泛应用。 官方给MEF下的定义:

Managed Extensibility Framework(MEF)是.NET平台下的一个扩展性管理框架,它是一系列特性的集合,包括依赖注入(DI)以及Duck Typing等。MEF为开发人员提供了一个工具,让我们可以轻松的对应用程序进行扩展并且对已有的代码产生最小的影响,开发人员在开发过程中根据功能要求定义一些扩展点,之后扩展人员就可以使用这些扩展点与应用程序交互;同时MEF让应用程序与扩展程序之间不产生直接的依赖,这样也允许在多个具有同样的扩展需求之间共享扩展程序。

概念

MEF 有几个基本核心概念:
可组合的部件(或简称“Part”)— 一个部件向其他部件提供服务,或使用其他部件提供的服务。MEF 中的部件可来自任何位置(应用程序内部或外部);从 MEF 的角度来看,这并无区别。
导出 — 导出是部件提供的服务。某个部件提供一个导出时,称为该部件导出 该服务。
导入 — 导入是部件使用的服务。某个部件使用一个导入时,称为该部件导入 该服务。
约定 — 约定是导出或导入的标识符。导出程序指定其提供的字符串约定,导入程序指定其需要的约定。MEF 从要导出和导入的类型派生约定名称,因此在大多数情况下,您不必考虑这一点。
组合 — 部件由 MEF 组合,MEF 将部件实例化,然后使导出程序与导入程序相匹配。

简短说一下MEF的工作原理,MEF的核心包括一个catalog和一个CompositionContainer。category用于发现扩展,而container用于协调创建和梳理依赖性。每个可组合的Part提供了一个或多个Export,并且通常依赖于一个或多个外部提供的服务或 Import。

MEF入门:寻找变化点

MEF是怎样工作的呢?我们来看最简单的“Hello,World”:
[code lang="C#"]
static void Main(string[] args)
{
      Console.WriteLine("Hello,World.");
}


如果某些业务要求,该字符串将来可能发生变化,也就是说这里有可能是一个变化点,也是一个扩展点,那我们现在使用MEF对其进行重新设计,我们将会把这个过程分成两个部分,一部分用于提供字符串:
[code lang="C#"]
    public class StringProvider
    {
        [Export("Message")]
        public String Output
        {
            get { return "Hello,World"; }
        }
    }


我们为Output属性添加了一个Export特性,这标识着此处有一个输出,它使的MEF能够对其进行识别。
我们再定义一个输入,即用来消费该字符串,同样是一个简单的属性,不过这次添加的是Import特性:
[code lang="C#"]
    public class Client
    {
        [Import("Message")]
        private String message=null;
         public void print()
        {
            Console.WriteLine(this.message);
        }
    }

这样就完成了导入导出的接口与实现的开发及特性配置。就剩下一步组合了:
[code lang="C#"]
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace cc.ejb.blogs.example
{
    class Program
    {
        static void Main(string[] args)
        {
            CompositionContainer container = new CompositionContainer();
            CompositionBatch batch = new CompositionBatch();
            Client client = new Client();
            batch.AddPart(new StringProvider());
            batch.AddPart(client);
            container.Compose(batch);

        }
    }
}


这里我们创建了一个组合容器对象(CompositionContainer)的实例,然后将需要组合的部件(Parts)添加到容器中即可。实际应用中,通常会定义许多导入(Import)和导出(Export)部件,这里MEF提供了不同的组合方式, 可以通过程序集(AssemblyCatalog)或目录(DirectoryCatalog) 或二者结合等方式绑定可扩展的对象。

通过程序集:
[code lang="C#"]
CompositionContainer container = new CompositionContainer(new AssemblyCatalog(typeof(Client).Assembly));
Client client = new Client();
container.SatisfyImportsOnce(client);
client.print();


也可以使用DirectoryCatalog,则在该目标路径下的所有程序集都会被遍历查询:
[code lang="C#"]
CompositionContainer container = new CompositionContainer(new DirectoryCatalog(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),"*.exe"));
Client client = new Client();
container.SatisfyImportsOnce(client);
client.print();

稍提一下, DirectoryCatalog默认的SearchPattern是"*.dll",这里改成"*.exe",这样我们的assembly才会被正确加载。

也可以二者相结合:
[code lang="C#"]
AggregateCatalog catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Client).Assembly));
CompositionContainer _container = new CompositionContainer(catalog);

posted @ 2011-06-02 23:34  小y  阅读(2016)  评论(0编辑  收藏  举报