MEF的学习笔记

为何要使用MEF 架构

  在商业应用软件开发过程当中,对于各个软件项目,都须要创建相应的系统框架,为了更好的规范系统的开发,提升生产效率,应该在公司级别制定相应的API标准。这些API标准将站在系统架构层次,以一样一个核心框架构建出不一样的商业应用。 框架

  对于各个商业应用中存在花样繁多的需求,同时又存在一些公用的模块,为了将这些可变的和相对稳定的功能模块有机的整合在一个系统框架下,那么就须要实现系统框架的可自定义插件开发。目前在MEF以前,业界也在大量的使用如 Castle Windsor、Structure Map、Spring.Net 以及Unity等依赖注入方式实现插件开发。而这些体系在.net平台中应用案例较少,在目前公司来讲,基本仍是空白,所以选择MEF这样一个全新的技术方案,相对其余方案门槛较低。 函数

MEF概念的理解
可组合的部件(或简称“Part”):一个部件能够向其余部件提供服务,也可使用其余部件提供的服务,它能够存在任何位置,能够是Web服务,外部系统,本系统。 学习

导出:导出是服务提供者 测试

导入:导入是服务使用者 this

约定:服务提供者与使用者之间使用的标示符,相似于身份识别。 .net

组合:对部件实例化,创建组合关系,是的导出部件和导入部件相匹配。 插件

MEF的工做原理 3d

  MEF的核心包括一个catalog(目录)和一个CompositionContainer(组合容器)。category用于发现扩展,而container用于协调建立和梳理依赖性。每一个可组合的Part提供了一个或多个Export(导出),而且一般依赖于一个或多个外部提供的服务或 Import(导入)。 orm

MEF的Demo

  demo1:宿主mef ,学习compose的过程以及部件基本的特性标记

  1.定义服务接口

interface IGetString
    {
        void WriteString();
    }

  2.定义服务的实现

[Export(typeof(IGetString))]
    class GetString :IGetString
    {
        public void WriteString()
        {
            Console.WriteLine("Hello Mef demo1!");
        }
    }

  3.配置宿主程序

class Program
    {
        /// <summary>
        /// 导入部件
        /// </summary>
        [Import(typeof(IGetString))]
        public IGetString Service { get; set; }
        
        //组合部件
        void Compose()
        {
            var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catelog);
            container.ComposeParts(this);
        }

        static void Main()
        {
            Program p = new Program();
            p.Compose();
            p.Service.WriteString();
            Console.ReadLine();
        }
    }

  4.运行效果

2012092614310569[1]

  demo2:多个部件的组合,学习ImportMany

  当同一个接口有多个实现的时候,MEF提供了ImportMany的方式,将实现多个

[Export(typeof(IGetString))]
    class GetString1 :IGetString
    {
        public void WriteString()
        {
            Console.WriteLine("Hello string1!");
        }
    }
    [Export(typeof(IGetString))]
    class GetString2 : IGetString
    {
        public void WriteString()
        {
            Console.WriteLine("Hello string2!");
        }
    }

  宿主程序代码:

class Program
    {
        /// <summary>
        /// 导入
        /// </summary>
        [ImportMany(typeof(IGetString))]
        public IEnumerable<IGetString> Service { get; set; }
        /// <summary>
        /// 组合
        /// </summary>
        void Compose()
        {
            var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catelog);
            container.ComposeParts(this);
        }

        static void Main()
        {

            Program p = new Program();
            p.Compose();
            foreach (var server in p.Service)
                server.WriteString();
            Console.ReadLine();
        }
    }

  运行效果: 

2012092614430187[1]

  demo3:多个部件和契约的配合

    对export添加字符串标示信息

[Export("txt",typeof(IGetString))]
    class GetString :IGetString
    {
        public void WriteString()
        {
            Console.WriteLine("Hello string1!");
        }
    }
    [Export("db",typeof(IGetString))]
    class GetString2 : IGetString
    {
        public void WriteString()
        {
            Console.WriteLine("Hello string2!");
        }
    }

    宿主程序导入部分一样增长字符串信息,与导出部件保持一致

class Program
    {
        /// <summary>
        /// 导入
        /// </summary>
        [Import("db",typeof(IGetString))]
        public IGetString Service { get; set; }
        /// <summary>
        /// 组合
        /// </summary>
        void Compose()
        {
            var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catelog);
            container.ComposeParts(this);
        }

        static void Main()
        {

            Program p = new Program();
            p.Compose(); 
            p.Service.WriteString();
            Console.ReadLine();
        }
    }

    运行效果:

2012092614494284[1]

  demo4:Import和Export的应用场景

  在MEF中,导入和导出能够应用在类,字段,属性,方法,并容许多个部件同时实现一个接口,和继承的特性。

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")]
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method,
                        AllowMultiple = true, Inherited = false)]
        public class ExportAttribute : Attribute
        {
            //......
        }

    导出属性、字段或方法

class GetString 
    {
        /// <summary>
        /// 导出属性
        /// </summary>
        [Export("txt")]
        public string GetString1
        {
            get { return "this is a fileds!"; }
        }

        
        [Export(typeof(Action<string>))]
        public void GetString2(string name)
        {
            Console.WriteLine(name);
        }

    }

    导入属性、字段或方法

     ///// <summary>
        ///// 导入
        ///// </summary>
        [Import("txt")]
        public string WriteString1 { get; set; } 

        [Import(typeof(Action<string>))]
        public Action<string> WriteString2 { get; set; }

    宿主程序

      /// <summary>
        /// 组合
        /// </summary>
        void Compose()
        {
            var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catelog);
            container.ComposeParts(this);
        }

        static void Main()
        {

            Program p = new Program();
            p.Compose();
            Console.WriteLine(p.WriteString1); 
            p.WriteString2("this is a parameter");
            Console.ReadLine();
        }

    输出结果:

2012092615563251[1]

  demo5:组合部件的嵌套

    在导出部件中进行了导入操做

   /// <summary>
    /// 服务接口
    /// </summary>
    interface IGetString
    {
        void WriteString();
    }
    /// <summary>
    ///导出部件
    /// </summary>
    [Export("txt",typeof(IGetString))]
    class GetString1 :IGetString
    {
        public void WriteString()
        {
            Console.WriteLine("Hello string1!");
        }
    }
    /// <summary>
    /// 导出部件
    /// </summary>
    [Export("db",typeof(IGetString))]
    class GetString2 : IGetString
    {
        public void WriteString()
        {
            Console.WriteLine("Hello string2!");
        }
    }
    /// <summary>
    /// 导出部件导入了其余部件
    /// </summary>
   [Export]
    class Getstring
    {
        [Import("txt",typeof(IGetString))]
        public IGetString Txt { get; set; }
        [Import("db", typeof(IGetString))]
        public IGetString Db { get; set; }
    }

      宿主程序:

        /// <summary>
        /// 导入
        /// </summary>
        [Import]
        public Getstring Service { get; set; }
        /// <summary>
        /// 组合
        /// </summary>
        void Compose()
        {
            var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catelog);
            container.ComposeParts(this);
        }

        static void Main()
        {

            Program p = new Program();
            p.Compose();
            //根据最近一层的服务提供输出
            p.Service.Txt.WriteString();
            p.Service.Db.WriteString();
            Console.ReadLine();
        }

    测试结果:

2012092616173765[1]

  demo6:组合部件的延迟加载

  部件准备工做

   /// <summary>
    /// 服务接口
    /// </summary>
    interface IGetString
    {
        void WriteString();
    }
    /// <summary>
    ///导出部件
    /// </summary>
    [Export("txt",typeof(IGetString))]
    class GetString1 :IGetString
    {
        private string initTime;
        public GetString1()
        {
            initTime = DateTime.Now.ToString("hh:mm:ss:");
        }
        public void WriteString()
        {
            Console.WriteLine("部件1初始化时间:\"{0}\"",initTime);
        }
    }
    /// <summary>
    /// 导出部件
    /// </summary>
    [Export("db",typeof(IGetString))]
    class GetString2 : IGetString
    {
        private string initTime;
        public GetString2()
        {
            initTime = DateTime.Now.ToString("hh:mm:ss:");
        }
        public void WriteString()
        {
            Console.WriteLine("部件2初始化时间:\"{0}\"", initTime);
        }
    }
    

    导入

        /// <summary>
        /// 导入
        /// </summary>
        [Import("txt",typeof(IGetString))]
        public  IGetString  Service1 { get; set; }
        /// <summary>
        /// 导出
        /// </summary>
        [Import("db", typeof(IGetString))]
        public Lazy<IGetString> Service2 { get; set; }

    宿主程序:

        static void Main()
        {

            Program p = new Program();
            //组合部件工做
            p.Compose(); 
            System.Threading.Thread.Sleep(2000);
           
            p.Service1.WriteString();
            //经过延迟加载,时间间隔为2秒
            p.Service2.Value.WriteString();
            Console.ReadLine();
        }

   输出效果:

2012092616372452[1]

  demo7:组合部件的继承

    导出部件,在接口中使用InheritedExport特性,在实现类中将省略Export标记

    /// <summary>
    /// 继承导出特性
    /// </summary>
    [InheritedExport]
    interface IGetString
    {
        void WriteString();
    }
    /// <summary>
    ///继承导出功能
    /// </summary> 
    class GetString  :IGetString
    { 
        public void WriteString()
        {
            Console.WriteLine("这是继承的导出部件");
        }
    }

  宿主程序:

        /// <summary>
        /// 导入
        /// </summary>
        [Import]
        public  IGetString  Service  { get; set; }
        
        /// <summary>
        /// 组合
        /// </summary>
        void Compose()
        {
            var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catelog);
            container.ComposeParts(this);
        }

        static void Main()
        {

            Program p = new Program();
            //组合部件工做
            p.Compose();  
           
            p.Service.WriteString(); 
            Console.ReadLine();
        }

  输出效果:
2012092616444553[1]

组合容器(CompositionContainer)和目录(Catalog)

 通过前面的demo练习,咱们已经了解了MEF中的导入(Import)和导出(Export)。在本系列的第一篇文章中咱们知道MEF其实还包括另外两个核心内容:组合容器(CompositionContainer)和目录(Catalog)。

  在宿主程序中,咱们须要经过组合容器和目录,将部件功能引入到当前的宿主程序应用中。

        /// <summary>
        /// 组合
       /// </summary>
        void Compose()
        {
            var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catelog);
            container.ComposeParts(this);
        }

组合容器:比较经常使用的有CompositionContainer,而有时候会须要用到CompositionBatch,这里讲不作讲解。

目录:Assembly Catalog(程序集目录),Directory Catalog,Aggregate Catalog,Type Catalog,和仅使用在Silverlight中得目录Deployment Catalog( Silverlight only),Filtered Catalog.其中将着重讲解Assembly Catalog(程序集目录),Directory Catalog,Aggregate Catalog。

1.Assembly Catalog

能够在给定的Assembly 发现全部的导出部件,使用类型AssemblyCatalog

2.Directory Catalog

它能够在给定的目录(路径,相对路径或绝对路径)中发现导出部件,使用类型DirectoryCatalog。若是你使用的是相对路径,则相对的是当前AppDoamin的基路径。DirectoryCatalog只会对给定目录进行一次性扫描,目录发生变化是容器不会主动刷新,若是须要刷新给定的目录须要调用方法:Refresh() ,当目录刷新时,容器也会从新组合部件。这个一般将一些动态连接库(.dll)做为部件导入到宿主程序中,能够灵活将DirectoryCatalog中的dll增长或移除,以对同一个API实现不一样的应用。

  demo8:使用Directory Catalog

  在项目中开发导出部件:经过添加类库的方式,生成相应功能的.dll文件,其中须要遵守一个API标准(IGetString)。

2012092617331833[1]

  1.经过MEFDemo8Service,定义API:

2012092617361785[1]

    /// <summary>
    /// 定义API,同时定义为继承导出方式
    /// </summary>
    [InheritedExport]
    public interface IGetString
    {
        void OutPut();
    }

  2.经过MEFDemoPart1和Part2分别实现相应的接口

2012092617363168[1]

    分别实现了部件1和部件2的方法

    /// <summary>
    /// 实现接口
    /// </summary>
    public class GetString:MEFDemo8Service.IGetString
    {
        public void OutPut()
        {
            Console.WriteLine("执行了部件1的方法"); 
        }
    }

  3.在宿主程序中,添加API的引用以及添加分别生成part1和part2的.dll文件

2012092617532039[1]

其中Lib文件夹中的dll将属性设置为内容,并复制

在宿主程序中,经过导入,组合(使用DirectoryCatalog,指定Lib文件夹),再经过Main的执行,显示出具体的实现内容,代码以下:

    class Program
    {
        /// <summary>
        /// 导入部件
        /// </summary>
        [ImportMany(typeof(MEFDemo8Service.IGetString))]
        public IEnumerable<MEFDemo8Service.IGetString> Service { get; set; }
        
        //组合部件
        void Compose()
        {
            var catelog = new DirectoryCatalog("Lib");
            var container = new CompositionContainer(catelog);
            container.ComposeParts(this);
        }

        static void Main()
        {
            Program p = new Program();
            p.Compose();
            foreach (var server in p.Service)
                server.OutPut();
            Console.ReadLine();
        }

  最终的执行效果:

2012092617583564[1]

  当在Lib中移出了部件1

2012092617590437[1]

  最终效果:

2012092617593567[1]

  当部件添加到Lib中,又获得了最开始的效果。

因而可知,使用Lib,能够将咱们的具体实现经过物理方式隔离,在须要的时候添加,不须要的时候移除便可。

3.Aggregate Catalog

汇集目录,有时候咱们使用单一的Assembly Catalog和Directory Catalog并不能解决咱们的需求,咱们可能须要同时使用到他们,这时候咱们即可使用Aggregate Catalog,咱们能够将Assembly Catalog和Directory Catalog同时添加到目录中,这种添加能够经过构造函数实现,也能够经过目录集合的添加方法来实现:catalog.Catalogs.Add(...)。汇集目录使用类型AggregateCatalog

MEF带来的联想

  留给你们来回答吧!!!

,

相关文章
相关标签/搜索