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. 移动端——页面点击( touchend -> click)

    手机端页面好多要注意的,点击事件就是其中一个: 在手机端页面中使用 click,安卓手机能正常实现点击效果,可是苹果手机不能点击:用 touchend 代替 click,苹果手机中能点击,但是可能出现 ...

  2. CentOS7重启后resolv.conf被重置的解决方案

    近期在修改一台CentOS7服务器的dns时发现只要重启服务器DNS就会被强制还原,解决方案如下: 1.首先在网卡设置中修改NM_CONTROLLED的值: 修改文件/etc/sysconfig/ne ...

  3. NiewPatchDrawable

    在使用AS的时候要注意以下几点: 1.点9图不能放在mipmap目录下,而需要放在drawable目录下! 2.AS中的.9图,必须要有黑线,不然编译都不会通过 xml定义NinePatchDrawa ...

  4. Linux通过AIO进行异步读文件

    下面列出源代码: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <a ...

  5. git clone https://chromium.googlesource.com/失败

    一.现象     连接着vpn,网页上可以直接打开网站,但是使用terminal 执行git clone https://chromium.googlesource.com/xxxx时,     报错 ...

  6. 建立第一个Django工程---linux中的python

    建立第一个Django工程 环境: ip: 192.168.0.92 系统:centos7.5 安装django pip install django 启动一个HelloWorld工程 django- ...

  7. wp-query调用前几篇文章的方法

    ---恢复内容开始--- 利用强大的wp-query函数调用指定分类下的前几篇文章,下面的代码表示调用的是分类ID4下的前两篇文章. <?php $cunt_wenzhen = array('c ...

  8. Jetson TX2 不同的工作模式

    Jetson TX2 有五种工作模式,下面介绍这几种工作模式下电压.频率以及如何启动. 原理图 几种不同的工作模式 mode mode name Denver Frequency ARM Freque ...

  9. jquery中 $.expr使用实例介绍

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. C#进阶系列—WebApi

    参考学习优秀博客地址:http://www.cnblogs.com/landeanfen/p/5177176.html