MEF和CompositionContainer学习

最近在研究项目平台启动,发现使用的是CompositionContainer,就对其起了兴趣,初步研究了一下,发现时MS提供的扩展性框架 MEF,下面是本人的一点心得和大家共同探讨。

MEF定义:

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

MEF组件,以Part形式,同事有Import和Export功能。

MEF导入导出:导入导出的类都继承同一个接口,然后把类实例化,最后在总的容器里导入。

接下来用代码实战了:

1:首先定义一个Metadata,可以附加自身附件信息。
元数据可用于将导出的对象的属性传递到导入部件。 导入部件可以使用此数据来决定要使用哪些导出,或收集有关导出的信息而不必构造导出。元数据视图接口必须只有属性,并且这些属性
必须具有 get 访问器。

public interface IPartInformation
    {
        string Name { get; }

        string Version { get; }

        Type PartType { get; }

        [DefaultValue(null)]
        Type[] Dependencies { get; }

        string Author { get; }

        string Contact { get; }

        string Date { get; }

        string Description { get; }

        string Comment { get; }
    }

2:定义一个公共接口,供导入导出继承

public interface ISinglePart
    {
    }

3:增加一个Class Library继承ISinglePart,同时标注Metadata和Export标签

    [Export(typeof(ISinglePart))]
    [ExportMetadata("Name","123")]
    [ExportMetadata("PartType", typeof(Class1))]
    [ExportMetadata("Author", "123")]
    [ExportMetadata("Contact", "123")]
    [ExportMetadata("Version", "2.0.0.0")]
    [ExportMetadata("Date", "2013/03/28")]
    [ExportMetadata("Description", "1234")]
    [ExportMetadata("Comment", "12345")]
    public class Class1:ISinglePart
    {
    }

4:增加MEF发现部件的方式:使用AssemblyCatalog 在当前程序集发现部件。

public class RecursiveDirectoryCatalog:ComposablePartCatalog
    {
        private AggregateCatalog _innerCatalog = null;

        public RecursiveDirectoryCatalog(string basePath, params string[] dirPaths)
        {
            var fullDirPaths = from p in dirPaths
                               let d = Path.IsPathRooted(p) ? p : Path.Combine(basePath, p)
                               select d;

            var asmCatalogs = from p in fullDirPaths
                              from dll in Directory.GetFiles(p, "*.dll", SearchOption.AllDirectories)
                              let cb = new AssemblyName().CodeBase = dll
                              where AssemblyHelp.IsManageDll(dll)
                              select new AssemblyCatalog(cb);

            _innerCatalog = new AggregateCatalog(asmCatalogs.OfType<ComposablePartCatalog>());
        }

        public override IQueryable<ComposablePartDefinition> Parts
        {
            get { return _innerCatalog.Parts; }
        }

        
    }

5:组合部件
在加载完部件之后,要把它们放到一个CompositionContainer容器中。

var catalog = new RecursiveDirectoryCatalog(basePath, partDirectories);
this.Container = new CompositionContainer(catalog);

6:导入程序负责指定将使用的元数据视图(如果有)。 包含元数据的导入将声明为延迟导入,其元数据接口作为 Lazy<T,T> 的第二个类型参数。 下面的类导入前面的部件以及元数据。

创建策略
当部件指定执行导入和组合时,组合容器将尝试查找匹配的导出。 如果它将导入与导出成功匹配,则导入成员将设置为导出的对象的实例。 导出部件的创建策略控制此实例来源于何处。导
入和导出都可从值 Shared、NonShared 或 Any 中指定部件的创建策略。 导入和导出的默认值均为 Any。

[ImportMany(RequiredCreationPolicy = CreationPolicy.Shared)]
public IEnumerable<Lazy<TSingle, IPartInformation>> SingleParts { get; private set; }

7:将指定部分加入CompositionBatch物件中,在容器上执行组合,包括指定的 CompositionBatch 中的更改,这样就能把Class1以及其Metadata导入至SingleParts中

var batch = new CompositionBatch();
batch.AddExportedValue(owner);
batch.AddExportedValue(this.Container);
batch.AddPart(this);

8:关于生命周期和释放

由于部件承载于组合容器中,因此其生命周期可能比普通对象更复杂,所以将只有拥有部件的容器才应对其调用 Dispose 方法。 容器本身实现 IDisposable,并且作为 Dispose 中其清理的
一部分,它将对拥有的所有部件调用 Dispose。

 public partial class AstPartContainer<TOwner, TSingle, TMultiple> : IDisposable
        where TOwner : class
        where TSingle : class
        where TMultiple : class
    {
    }

最后总结一下使用MEF,到底有什么用处?
假设项目中包含有大量的小组件,并且每一个组件都是都不同的开发小组开发的,甚至都无法提供源码,这样您就无法在不修改大量源码的情况下添加新的组件,但是如果使用MEF,对于同一
类型的组件继承同一个接口,定义导入导出,这样只需要提供相关DLL,就能以Part的形式加入容器中,供开发人员调用。其具有很强灵活性的可扩展支持

posted @ 2013-03-29 10:55  gavin.huang  阅读(873)  评论(1编辑  收藏  举报