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

IOC之MEF学习的更多相关文章

  1. .NET自带IOC容器MEF之初体验

    .NET自带IOC容器MEF之初体验   本文主要把MEF作为一种IOC容器进行讲解,.net中可用的IOC容器非常多,如 CastleWindsor,Unity,Autofac,ObjectBuil ...

  2. C#可扩展编程之MEF学习笔记(五):MEF高级进阶

    好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...

  3. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

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

    前面说完了导入和导出的几种方法,如果大家细心的话会注意到前面我们导出的都是类,那么方法和属性能不能导出呢???答案是肯定的,下面就来说下MEF是如何导出方法和属性的. 还是前面的代码,第二篇中已经提供 ...

  5. C#可扩展编程之MEF学习笔记(二):MEF的导出(Export)和导入(Import)

    上一篇学习完了MEF的基础知识,编写了一个简单的DEMO,接下来接着上篇的内容继续学习,如果没有看过上一篇的内容, 请阅读:http://www.cnblogs.com/yunfeifei/p/392 ...

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

    在文章开始之前,首先简单介绍一下什么是MEF,MEF,全称Managed Extensibility Framework(托管可扩展框架).单从名字我们不难发现:MEF是专门致力于解决扩展性问题的框架 ...

  7. C#可扩展编程之MEF学习

    MEF系列文章: C#可扩展编程之MEF学习笔记(一):MEF简介及简单的Demo C#可扩展编程之MEF学习笔记(二):MEF的导出(Export)和导入(Import) C#可扩展编程之MEF学习 ...

  8. Spring入门IOC和AOP学习笔记

    Spring入门IOC和AOP学习笔记 概述 Spring框架的核心有两个: Spring容器作为超级大工厂,负责管理.创建所有的Java对象,这些Java对象被称为Bean. Spring容器管理容 ...

  9. [转]MEF学习

    MEF学习 :http://www.cnblogs.com/comsokey/p/MEF1.html C#可扩展编程之MEF学习笔记(一):MEF简介及简单的Demo C#可扩展编程之MEF学习笔记( ...

随机推荐

  1. [MySql]当虚拟机的IP地址自动更换后,JDBC使用原来的配置连不上MySql数据库时所报的异常。

    Communications link failure The last packet sent successfully to the server was 0 milliseconds ago. ...

  2. realsense data

    line: (43,350) (558,350) (-0.448628,-0.554531,0.995),(0,0,0) 0.713282 ------------------------------ ...

  3. 图像分割-Mask Scoring R-CNN

    转载:https://zhuanlan.zhihu.com/p/58291808 论文链接:https://arxiv.org/abs/1903.00241 代码链接:https://github.c ...

  4. Node.js使用Express实现Get和Post请求

    var express = require('express'); var app = express(); // 主页输出 "Hello World" app.get('/', ...

  5. iOS开发之—— 加密使用(MD5,base64,DES,AES)

    基本的单向加密算法: BASE64 严格地说,属于编码格式,而非加密算法 MD5(Message Digest algorithm 5,信息摘要算法)SHA(Secure Hash Algorithm ...

  6. python-Web-flask-路由和视图

    1 路由和视图: 简介: Flask框架包含两个核心:Werkzeug工具箱,Jinja2模板引擎 flask: 提供基本功能,属于轻量级 django: 提供相对完整功能,重量级 搭建虚拟环境 He ...

  7. 【世界之大,我用Python】Ubuntu 自动删除自带软件

    起因 Ubuntu是用来做开发程序的系统,如果存在大量的"垃圾"软件就不好了吧,不仅影响系统性能而且还会经常报错,所以每次安装完系统都会清理一次系统. 操作 我清理系统一般都是直接 ...

  8. Unity动态批处理和静态批处理学习

    本文转自:http://blog.csdn.net/lyh916/article/details/45725499,请点击链接查看楼主大神原文,尊重楼主版权. 参考链接:Unity圣典:http:// ...

  9. Impacket网络协议工具包介绍

    转载自FreeBuf.COM Impacket是一个Python类库,用于对SMB1-3或IPv4 / IPv6 上的TCP.UDP.ICMP.IGMP,ARP,IPv4,IPv6,SMB,MSRPC ...

  10. redis同步到磁盘