zoukankan      html  css  js  c++  java
  • IOC之MEF学习

    MEF原理上很简单,找出有共同接口的导入、导出。然后找到把导出的实例化,赋给导入。说到底MEF就是找到合适的类实例化,把它交给导入。
    Export 特性可修饰类、字段、属性或方法,而 Import 特性可修饰字段、属性或构造函数参数。为了使导入与导出匹配,导入和导出必须具有相同的协定。

    假设有一个类MyClass,它声明了可以导入插件的类型是IMyAddin。
    public class MyClass
    {
        [Import]
        public IMyAddin MyAddin { get; set; }
    }

    这里有一个类,它声明为导出。类型同样为IMyAddin。
    [Export(typeof(IMyAddin))]
    public class MyLogger : IMyAddin { }

    这样我们使用MyAddin属性的时候就可以获得到MyLogger的实例。

    发现部件
      MEF提供三种方式发现部件
        AssemblyCatalog 在当前程序集发现部件。
        DirectoryCatalog 在指定的目录发现部件。
        DeploymentCatalog 在指定的XAP文件中发现部件(用于silverlight)

    当通过不同方式发现部件的时候,还可以使用AggregateCatalog来把这些部件聚合到一起。
    var catalog = new AggregateCatalog();
    //把从Program所在程序集中发现的部件添加到目录中
    catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
    //把从指定path发现的部件添加到目录中
    catalog.Catalogs.Add(new DirectoryCatalog("C:\Users\v-rizhou\SimpleCalculator\Extensions"));

    如何组合部件?
    在加载完部件之后,要把它们放到一个CompositionContainer容器中。
    var container = new CompositionContainer(catalog)

    通过调用容器的ComposeParts()方法可以把容器中的部件组合到一起。
    container.ComposeParts(this);

    下面我们来看一下一个接口被多个类实例化
    当一个接口被多个类实例化时,用ImportMany 声明,具体如下
    [ImportMany]
    public IEnumerable<IHelloWord> HelloWord { get; set; }

    导入和导出的继承
    如果某个类继承自部件,则该类也可能会成为部件。 导入始终由子类继承。 因此,部件的子类将始终为部件,并具有与其父类相同的导入。通过使用 Export 特性的声明的导出不会由子类继承。 但是,部件可通过使用 InheritedExport 特性继承自身。 部件的子类将继承并提供相同的导出,其中包括协定名称和协定类型。 与 Export 特性不同,InheritedExport 只能在类级别(而不是成员级别)应用。 因此,成员级别导出永远不能被继承。

    下面四个类演示了导入和导出继承的原则。 NumTwo 继承自 NumOne,因此 NumTwo 将导入 IMyData。 普通导出不会被继承,因此 NumTwo 将不会导出任何内容。 NumFour 继承自NumThree。 由于 NumThree 使用了 InheritedExport,因此 NumFour 具有一个协定类型为 NumThree 的导出。 成员级别导出从不会被继承,因此不会导出 IMyData。
    [Export]
    public class NumOne
    {
        [Import]
        public IMyData MyData { get; set; }
    }

    public class NumTwo : NumOne
    {
        //导入会被继承,所以NumTwo会有导入属性 IMyData

        //原始的导出不能被继承,所以NumTwo不会有任何导出。所以它不会被目录发现
    }

    [InheritedExport]
    public class NumThree
    {
        [Export]
        Public IMyData MyData { get; set; }

        //这个部件提供两个导出,一个是NumThree,一个是IMyData类型的MyData
    }

    public class NumFour : NumThree
    {
        //因为NumThree使用了InheritedExport特性,这个部件有一个导出NumThree。

        //成员级别的导出永远不会被继承,所以IMydata永远不是导出
    }

    如何避免被发现?
    在某些情况下,您可能需要防止部件作为目录的一部分被发现。 例如,部件可能是应从中继承(而不是使用)的基类。 可通过两种方式来实现此目的。 首先,可以对部件类使用abstract 关键字。 尽管抽象类能够向派生自抽象类的类提供继承的导出,但抽象类从不提供导出。如果无法使类成为抽象类,您可以使用 PartNotDiscoverable 特性来修饰它。 用此特性修饰的部件将不会包括在任何目录中。如下程序中,只有DataOne可以被发现。
    [Export]
    public class DataOne
    {
        //This part will be discovered
        //as normal by the catalog.
    }

    [Export]
    public abstract class DataTwo
    {
        //This part will not be discovered
        //by the catalog.
    }

    [PartNotDiscoverable]
    [Export]
    public class DataThree
    {
        //This part will also not be discovered
        //by the catalog.
    }

    元数据和元数据视图

    自定义导出特性
    可以对基本导出特性 Export 和 InheritedExport 进行扩展,以包括元数据作为特性属性。 在将类似的元数据应用于多个部件或创建元数据特性的继承树时,此方法十分有用。


    MEF在MVC中的使用
    在MVC的项目中,IOC组件是通过 DependencyResolver类中的 SetResolver(IDependencyResolver resolver) 方法来向MVC提供注册点的,所以我们只需要实现一个 IDependencyResolver 接口的MEF实现类,即可完成MEF在MVC中的注册工作。

      另外考虑到Web应用程序的无状态性,即每次访问都是独立进行的,所以IOC组件产生的对象实例也必须唯一,否则不同用户的操作就可能串线,产生相互干扰。在这里,我们使用HttpContext.Current.Items集合来保存 组合容器CompositionContainer的实例,以使每个用户的数据保持独立,并且同一用户的同一个Http请求中使用同一对象实例。另外考虑到可能会有某种情况下需要手动获取组合容器中的实例,把组合容器缓存到了当前上下文中的Application中。

    MefDependencySolver实现代码如下:
    /// <summary>
        /// MEF依赖关系解析类
        /// </summary>
        public class MefDependencySolver : IDependencyResolver
        {
            private readonly ComposablePartCatalog _catalog;
            private const string HttpContextKey = "MefContainerKey";

            public MefDependencySolver(ComposablePartCatalog catalog)
            {
                _catalog = catalog;
            }

            public CompositionContainer Container
            {
                get
                {
                    if (!HttpContext.Current.Items.Contains(HttpContextKey))
                    {
                        HttpContext.Current.Items.Add(HttpContextKey, new CompositionContainer(_catalog));
                    }
                    CompositionContainer container = (CompositionContainer)HttpContext.Current.Items[HttpContextKey];
                    HttpContext.Current.Application["Container"] = container;
                    return container;
                }
            }

            #region IDependencyResolver Members

            public object GetService(Type serviceType)
            {
                string contractName = AttributedModelServices.GetContractName(serviceType);
                return Container.GetExportedValueOrDefault<object>(contractName);
            }

            public IEnumerable<object> GetServices(Type serviceType)
            {
                return Container.GetExportedValues<object>(serviceType.FullName);
            }

            #endregion
        }
        
    在Global.asax.cs的Application_Start方法中初始化MEF容器,由于Web应用程序中只需要在DLL中查找匹配,所以只使用DirectoryCatalog即可。

     
    本文内容来源:
    http://www.cnblogs.com/ulex/p/4186881.html
    http://www.cnblogs.com/techborther/archive/2012/02/06/2339877.html
    http://www.cnblogs.com/guomingfeng/archive/2013/05/21/mvc-ioc-mef.html

  • 相关阅读:
    spark hbase
    Benchmark简介
    Flink的安装配置
    Hive入门及常用指令
    hadoop+yarn+hbase+storm+kafka+spark+zookeeper)高可用集群详细配置
    Linux最常用的命名
    数据库的零散的总结
    DBA总结
    MySQL主从架构配置
    mysql主从读写分离,分库分表
  • 原文地址:https://www.cnblogs.com/zhaow/p/9754150.html
Copyright © 2011-2022 走看看