zoukankan      html  css  js  c++  java
  • Managed Extensibility Framework(MEF) 自动发现式扩展框架

       Managed Extensibility Framework (后称MEF) 是.net 4.0中新增的一个功能特性. 是一个可以自动扩展的轻型框架.以下是MSDN的原版翻译.

    假设您是一个必须提供扩展性支持的大型应用程序的架构师。 您的应用程序必须包含大量可能需要的较小组件,并负责创建和运行这些组件。
    解决这一问题的最简单的方法是:将这些组件作为源代码包括在您的应用程序中,然后通过代码直接调用它们。 这种做法存在很多明显的缺陷。 最重要的是,您无法在不修改源代码的情况下添加新组件,这一限制在 Web 应用程序(举例来说)中也许能够接受,但在客户端应用程序中行不通。 同样存在问题的还有,您可能没有对组件的源代码的访问权,因为这些组件可能是由第三方开发的,而出于相同的原因,您也不允许第三方访问您的代码。
    一种稍微复杂的方法是:提供扩展点或接口,以允许应用程序与其组件相分离。 依据此模型,您可能会提供一个组件能够实现的接口,并提供一个 API 以使该接口能够与您的应用程序进行交互。 这一方法可解决需要源代码访问权的问题,但仍具有自己的难点。
    由于应用程序缺乏自己发现组件的能力,因此仍必须明确告知应用程序哪些组件可用并应加载。 这通常是通过在一个配置文件中显式注册可用组件来实现的。 这意味着,确保组件正确无误成为了一个日常维护问题,尤其是在执行更新操作的是最终用户而非开发人员的情况下。
    此外,各组件之间无法进行通信,除非是通过应用程序自身的严格定义的通道。 如果应用程序架构师未预计到需要某项通信,则通常是无法进行相应的通信的。
    最后,组件开发人员不得不硬依赖于包含他们实现的接口的程序集。 这样就很难在多个应用程序中使用同一个组件,另外,在为组件创建测试框架时也会造成问题。

    在没有MEF的情况下,通常是用反射来完成组件注册使用的.而反射有很多不便之处.需要应用程序知晓命名空间,程序程序集地址,如果是注册一个新组件,通常需要开发人员或专来的维护人员在配置文件中添加新配置.更糟的情况是可能需要修改代码.

    MEF可以通过自动发现机制来管理更新的程序集,无需专业人员参与. 可以轻松完成软件的自动更新升级

    以下代码演示MEF的使用,需要引用System.ComponentModel.Composition.dllSystem.Data.DataSetExtensions.dll

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    
    namespace SimpleCalculator3
    {
    
        //运算表达式
        public interface ICalculator
        {
            String Calculate(String input);
        }
    
        //运算的左右参加
        public interface IOperation
        {
            int Operate(int left, int right);
        }
    
        //运算符
        public interface IOperationData
        {
            Char Symbol { get; }
        }
    
        //每个运算的元数据是表示对应运算的符号,如 +、-、* 等。 
        //若要使加法运算可用,请将下面的类添加到模块或 SimpleCalculator 命名空间中
    
        //ExportAttribute 特性的功能与以前相同。 ExportMetadataAttribute 特性将元数据以名称/值对的形式附加到相应导出。 
        //尽管 Add 类可实现 IOperation,但并未显式定义实现 IOperationData 的类。 
        //相反,MEF 基于所提供元数据的名称使用属性隐式创建了一个类。 (这是访问 MEF 中的元数据的方法之一。) 
    
        //MEF 中的组合是递归的。 您显式组合 Program 对象,该对象会导入一个证明是 MySimpleCalculator 类型的 ICalculator。 
        //接下来,MySimpleCalculator 将导入 IOperation 对象的集合,
        //而该导入将在创建 MySimpleCalculator 时与 Program 的导入同时进行填充。 
        //如果 Add 类声明了进一步的导入,还必须填充接下来的导入,以此类推。 
        //任何未填充的导入将导致组合错误。 (不过,可以将导入声明为可选导入或为其分配默认值。) 
        [Export(typeof(IOperation))]
        [ExportMetadata("Symbol", '+')]
        class Add : IOperation
        {
            public int Operate(int left, int right)
            {
                return left + right;
            }
        }
    
        [Export(typeof(IOperation))]
        [ExportMetadata("Symbol", '-')]
        class Subtract : IOperation
        {
    
            public int Operate(int left, int right)
            {
                return left - right;
            }
    
        }
    
    
    
        [Export(typeof(ICalculator))]
        class MySimpleCalculator : ICalculator
        {
            //为了使 SimpleCalculator 能够扩展,它需要导入一组操作。 一般的 ImportAttribute 特性由一个且只由一个 ExportAttribute 填充。 
            //如果有多个导出可用,则组合引擎将生成错误。 
            //若要创建一个可由任意数量的导出填充的导入,可以使用 ImportManyAttribute 特性。 
            [ImportMany]
            //Lazy(Of T, TMetadata) 是 MEF 提供的用于保存对导出的间接引用的类型。 
            //在此,除了导出的对象本身以外,您还将获取导出元数据或描述导出的对象的信息。
            //每个 Lazy(Of T, TMetadata) 都包含一个 IOperation 对象(表示一个实际运算)和一个 IOperationData 对象(表示运算的元数据)。 
            IEnumerable<Lazy<IOperation, IOperationData>> operations;
    
    
            public String Calculate(String input)
            {
                int left;
                int right;
                Char operation;
                int fn = FindFirstNonDigit(input); //finds the operator
                if (fn < 0) return "Could not parse command.";
    
                try
                {
                    //separate out the operands
                    //初始步骤将输入字符串分析为左右操作数和运算符。
                    left = int.Parse(input.Substring(0, fn));
                    right = int.Parse(input.Substring(fn + 1));
                }
                catch
                {
                    return "Could not parse command.";
                }
    
                operation = input[fn];
    
                //在 foreach 循环中,将对 operations 集合的每个成员进行检查。 
                //这些对象的类型是 Lazy(Of T, TMetadata),可分别使用 Metadata 属性和 Value 属性来访问其元数据值和导出的对象。 
                //在此例中,如果发现 IOperationData 对象的 Symbol 属性是一个匹配项,则计算器将调用 IOperation 对象的 Operate 方法并返回结果。
                foreach (Lazy<IOperation, IOperationData> i in operations)
                {
                    if (i.Metadata.Symbol.Equals(operation)) return i.Value.Operate(left, right).ToString();
                }
                return "Operation Not Found!";
            }
    
            private int FindFirstNonDigit(String s)
            {
    
                for (int i = 0; i < s.Length; i++)
                {
                    if (!(Char.IsDigit(s[i]))) return i;
                }
                return -1;
            }
    
    
        }
    
    
        class Program
        {
            private CompositionContainer _container;
    
            [Import(typeof(ICalculator))]
            public ICalculator calculator;
    
    
            private Program()
            {
                //为了发现可用于组合容器的部件,组合容器将使用“目录”。 
                //目录就是一个对象,通过它可从某些源发现可用部件。 
                //MEF 提供了用于从提供的类型、程序集或目录发现部件的目录。
                //应用程序开发人员可以轻松地创建用于从其他源(如 Web 服务)发现部件的新目录。
    
    
                //An aggregate catalog that combines multiple catalogs
                var catalog = new AggregateCatalog();
                //Adds all the parts found in the same assembly as the Program class
                catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
                catalog.Catalogs.Add(new DirectoryCatalog("E:\\Temp"));
    
    
                //Create the CompositionContainer with the parts in the catalog
                _container = new CompositionContainer(catalog);
    
                //Fill the imports of this object
                try
                {
                    this._container.ComposeParts(this);
                }
                catch (CompositionException compositionException)
                {
                    Console.WriteLine(compositionException.ToString());
                }
            }
    
    
            static void Main(string[] args)
            {
                Program p = new Program(); //Composition is performed in the constructor
                String s;
                Console.WriteLine("Enter Command:");
                while (true)
                {
                    s = Console.ReadLine();
                    Console.WriteLine(p.calculator.Calculate(s));
                }
                
    
            }
        }
    }


    扩展SimpleCalculator

    新建一个输出类型为类库的项目,添加以下代码

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.Composition;
    namespace ExtendedOperations {
    
        [Export(typeof(SimpleCalculator3.IOperation))]
        [ExportMetadata("Symbol", '%')]
        public class Mod : SimpleCalculator3.IOperation
        {
            public int Operate(int left, int right)
            {
                return left % right;
            }
        }
    
    }

    编译完成后将生成的dll文件copy到E://Temp目录下,运行第一个项目,可以测试扩展的%运算

    曾经年少多少事 而今皆付谈笑中!

  • 相关阅读:
    dstat
    centos安装指定版本的golang
    APP防CC为什么复杂
    火狐浏览器的书签如何自动在新窗口打开?
    linux jdk版本随时切换
    centos7 yum安装java环境
    kangle清除缓存接口
    CC攻击原理及防范方法
    GET 和 POST 的区别 以及为什么 GET请求 比 POST请求 更快
    HTTP缓存机制
  • 原文地址:https://www.cnblogs.com/xuf22/p/2350754.html
Copyright © 2011-2022 走看看