MEF的学习笔记

为什么要使用MEF

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

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

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

导出:导出是服务提供者

导入:导入是服务使用者

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

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

MEF的工作原理

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

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.运行效果

  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();
        }
    }

  运行效果: 

  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();
        }
    }

    运行效果:

  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();
        }

    输出结果:

  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();
        }

    测试结果:

  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();
        }

输出效果:

  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();
        }

  输出效果:

组合容器(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)。

  1.通过MEFDemo8Service,定义API:

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

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

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

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

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

其中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();
        }

  最终的执行效果:

  当在Lib中移出了部件1

最终效果:

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

由此可见,使用Lib,可以将我们的具体实现通过物理方式隔离,在需要的时候添加,不需要的时候移除即可。

3.Aggregate Catalog

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

MEF带来的联想

  留给大家来回答吧!!!

,

时间: 2024-12-28 17:54:53

MEF的学习笔记的相关文章

C#可扩展编程之MEF学习笔记(一):MEF简介及简单的Demo

在文章开始之前,首先简单介绍一下什么是MEF,MEF,全称Managed Extensibility Framework(托管可扩展框架).单从名字我们不难发现:MEF是专门致力于解决扩展性问题的框架,MSDN中对MEF有这样一段说明: Managed Extensibility Framework 或 MEF 是一个用于创建可扩展的轻型应用程序的库. 应用程序开发人员可利用该库发现并使用扩展,而无需进行配置. 扩展开发人员还可以利用该库轻松地封装代码,避免生成脆弱的硬依赖项. 通过 MEF,不

C#可扩展编程之MEF学习笔记(三):导出类的方法和属性

前面说完了导入和导出的几种方法,如果大家细心的话会注意到前面我们导出的都是类,那么方法和属性能不能导出呢???答案是肯定的,下面就来说下MEF是如何导出方法和属性的. 还是前面的代码,第二篇中已经提供了下载链接,大家可以下载学习. 首先来说导出属性,因为这个比较简单,和导出类差不多,先来看看代码,主要看我加注释的地方,MusicBook.cs中的代码如下: using System; using System.Collections.Generic; using System.Linq; usi

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则 用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详细,自己在看它的文档和代码时写了一些demo和笔记,还有它实现的原理记录一下 学习Caliburn.Micro要有MEF和MVVM的基础 先说一下他的命名规则和引导类 以后我会把Caliburn.Micro的 Actions IResult,IHandle ICondu

生命游戏和细胞自动机的学习笔记

Last updated: 23rd. July, 2012 野比 2012 版权所有 (本文为学习笔记,知识浅薄.我会将学习中的实验记录和心得记录在此.) 欢迎对这方面感兴趣的爱好者一起研究. 寻求技术指导. 联系QQ:1429013154 我一直对人工智能很感兴趣,苦于数学基础太差,很多理论方面的东西理解起来十分吃力.最近又翻出以前的学习目标:人工生命.名字挺悬乎,其实很多人都曾和它有过交集,就算没有用到它进化出的好物种--智能机器人,至少也应该听过甚至中过它进化的恶劣物种--蠕虫病毒. 说

vector 学习笔记

vector 使用练习: /**************************************** * File Name: vector.cpp * Author: sky0917 * Created Time: 2014年04月27日 11:07:33 ****************************************/ #include <iostream> #include <vector> using namespace std; int main

jQuery学习笔记(一):入门

jQuery学习笔记(一):入门 一.JQuery是什么 JQuery是什么?始终是萦绕在我心中的一个问题: 借鉴网上同学们的总结,可以从以下几个方面观察. 不使用JQuery时获取DOM文本的操作如下: 1 document.getElementById('info').value = 'Hello World!'; 使用JQuery时获取DOM文本操作如下: 1 $('#info').val('Hello World!'); 嗯,可以看出,使用JQuery的优势之一是可以使代码更加简练,使开

[原创]java WEB学习笔记93:Hibernate学习之路---Hibernate 缓存介绍,缓存级别,使用二级缓存的情况,二级缓存的架构集合缓存,二级缓存的并发策略,实现步骤,集合缓存,查询缓存,时间戳缓存

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

Activiti 学习笔记记录(三)

上一篇:Activiti 学习笔记记录(二) 导读:上一篇学习了bpmn 画图的常用图形标记.那如何用它们组成一个可用文件呢? 我们知道 bpmn 其实是一个xml 文件

HTML&CSS基础学习笔记8-预格式文本

<pre>标签的主要作用是预格式化文本.被包围在 pre 标签中的文本通常会保留空格和换行符.而文本也会呈现为等宽字体. <pre>标签的一个常见应用就是用来表示计算机的源代码.当然你也可以在你需要在网页中预显示格式时使用它. 会使你的文本换行的标签(例如<h>.<p>)绝不能包含在 <pre> 所定义的块里.尽管有些浏览器会把段落结束标签解释为简单地换行,但是这种行为在所有浏览器上并不都是一样的. 更多学习内容,就在码芽网http://www.