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. 飞行姿态角度表示: heading pitch roll

    //创建初始化摄像机视图 var initialPosition=new Cesium.Cartesian3.fromDegrees(-73.998114468289017509, 40.674512 ...

  2. python操作MySQL数据库的三个模块

    python使用MySQL主要有两个模块,pymysql(MySQLdb)和SQLAchemy. pymysql(MySQLdb)为原生模块,直接执行sql语句,其中pymysql模块支持python ...

  3. SQL-W3School-高级:SQL NULL 函数

    ylbtech-SQL-W3School-高级:SQL NULL 函数 1.返回顶部 1. SQL ISNULL().NVL().IFNULL() 和 COALESCE() 函数 请看下面的 &quo ...

  4. shell生成指定范围随即整数

    #!/bin/bash function rand(){ min=$ max=$(($-$min+)) num=$( | cksum | awk -F ' ' '{print $1}') echo $ ...

  5. java中的异步处理和Feature接口(一)

    背景介绍想象这样一个场景:你可能希望为你的法国客户提供指定主题的热点报道.为实现这一功能,你需要向 谷歌或者Twitter的API请求所有语言中针对该主题最热门的评论,可能还需要依据你的内部算法 对它 ...

  6. 13 Flutter仿京东商城项目 商品列表筛选以及上拉分页加载更多

    ProductList.dart import 'package:flutter/material.dart'; import '../services/ScreenAdaper.dart'; imp ...

  7. EasyNetQ使用(二)【连接RabbitMQ,SSL连接,Logging】

    如果你连接过关系数据库,例如SQL Server.你会发现EasyNetQ处理connections有点奇怪.和关系数据库通讯一直都是通过client开始的.Client 打开一个连接, 发出一个SQ ...

  8. Flutter 圆形/圆角头像图片

    图片显示 1.本地图片 Image.asset加载项目资源包的图片 //先将图片拷贝到项目 images 目录中,然后在 pubspec.yaml文件配置文件相对路径到 assets Image.as ...

  9. 【ES 系列1】介绍与方案设计

    简介 ElasticSearch是一个高度可扩展的开源全文搜索和分析引擎.它允许您快速.近实时地存储.搜索和分析大量数据.它通常被用作驱动具有复杂搜索功能和需求的应用程序的底层引擎/技术.适用于需要大 ...

  10. 为 Exchange 2010 用户添加联系人头像

    一.修改AD架构 为了给联系人添加头像,实际是让联系人头像缩略图能够显示在全局地址列表 GAL 中,需要让其在全局编录(GC)中进行复制,默认情况下,对象的“thumbnailphoto”属性值不会在 ...