MEF的学习笔记
为什么要使用MEF
在商业应用软件开发过程中,对于各个软件项目,都需要建立相应的系统框架,为了更好的规范系统的开发,提高生产效率,应该在公司级别制定相应的API标准。这些API标准将站在系统架构层次,以同样一个核心框架构建出不同的商业应用。
对于各个商业应用中存在花样繁多的需求,同时又存在一些公用的模块,为了将这些可变的和相对稳定的功能模块有机的整合在一个系统框架下,那么就需要实现系统框架的可自定义插件开发。目前在MEF之前,业界也在大量的使用如 Castle Windsor、Structure Map、Spring.Net 以及Unity等依赖注入方式实现插件开发。而这些体系在.net平台中应用案例较少,在目前公司来说,基本还是空白,因此选择MEF这样一个全新的技术方案,相对其他方案门槛较低。
MEF概念的理解
可组合的部件(或简称“Part”):一个部件可以向其他部件提供服务,也可以使用其他部件提供的服务,它可以存在任何位置,可以是Web服务,外部系统,本系统。
导出:导出是服务提供者
导入:导入是服务使用者
约定:服务提供者与使用者之间使用的标示符,类似于身份识别。
组合:对部件实例化,建立组合关系,是的导出部件和导入部件相匹配。
MEF的工作原理
MEF的核心包括一个catalog(目录)和一个CompositionContainer(组合容器)。category用于发现扩展,而container用于协调创建和梳理依赖性。每个可组合的Part提供了一个或多个Export(导出),并且通常依赖于一个或多个外部提供的服务或 Import(导入)。
MEF的Demo
demo1:宿主mef ,学习compose的过程以及部件基本的特性标记
1.定义服务接口
interface IGetString
{
void WriteString();
}
2.定义服务的实现
[Export(typeof(IGetString))]
class GetString :IGetString
{
public void WriteString()
{
Console.WriteLine("Hello Mef demo1!");
}
}
3.配置宿主程序
class Program
{
/// <summary>
/// 导入部件
/// </summary>
[Import(typeof(IGetString))]
public IGetString Service { get; set; }
//组合部件
void Compose()
{
var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catelog);
container.ComposeParts(this);
}
static void Main()
{
Program p = new Program();
p.Compose();
p.Service.WriteString();
Console.ReadLine();
}
}
4.运行效果

demo2:多个部件的组合,学习ImportMany
当同一个接口有多个实现的时候,MEF提供了ImportMany的方式,将实现多个
[Export(typeof(IGetString))]
class GetString1 :IGetString
{
public void WriteString()
{
Console.WriteLine("Hello string1!");
}
}
[Export(typeof(IGetString))]
class GetString2 : IGetString
{
public void WriteString()
{
Console.WriteLine("Hello string2!");
}
}
宿主程序代码:
class Program
{
/// <summary>
/// 导入
/// </summary>
[ImportMany(typeof(IGetString))]
public IEnumerable<IGetString> Service { get; set; }
/// <summary>
/// 组合
/// </summary>
void Compose()
{
var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catelog);
container.ComposeParts(this);
}
static void Main()
{
Program p = new Program();
p.Compose();
foreach (var server in p.Service)
server.WriteString();
Console.ReadLine();
}
}
运行效果:

demo3:多个部件和契约的配合
对export添加字符串标示信息
[Export("txt",typeof(IGetString))]
class GetString :IGetString
{
public void WriteString()
{
Console.WriteLine("Hello string1!");
}
}
[Export("db",typeof(IGetString))]
class GetString2 : IGetString
{
public void WriteString()
{
Console.WriteLine("Hello string2!");
}
}
宿主程序导入部分同样增加字符串信息,与导出部件保持一致
class Program
{
/// <summary>
/// 导入
/// </summary>
[Import("db",typeof(IGetString))]
public IGetString Service { get; set; }
/// <summary>
/// 组合
/// </summary>
void Compose()
{
var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catelog);
container.ComposeParts(this);
}
static void Main()
{
Program p = new Program();
p.Compose();
p.Service.WriteString();
Console.ReadLine();
}
}
运行效果:

demo4:Import和Export的应用场景
在MEF中,导入和导出可以应用在类,字段,属性,方法,并允许多个部件同时实现一个接口,和继承的特性。
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method,
AllowMultiple = true, Inherited = false)]
public class ExportAttribute : Attribute
{
//......
}
导出属性、字段或方法
class GetString
{
/// <summary>
/// 导出属性
/// </summary>
[Export("txt")]
public string GetString1
{
get { return "this is a fileds!"; }
}
[Export(typeof(Action<string>))]
public void GetString2(string name)
{
Console.WriteLine(name);
}
}
导入属性、字段或方法
///// <summary>
///// 导入
///// </summary>
[Import("txt")]
public string WriteString1 { get; set; }
[Import(typeof(Action<string>))]
public Action<string> WriteString2 { get; set; }
宿主程序
/// <summary>
/// 组合
/// </summary>
void Compose()
{
var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catelog);
container.ComposeParts(this);
}
static void Main()
{
Program p = new Program();
p.Compose();
Console.WriteLine(p.WriteString1);
p.WriteString2("this is a parameter");
Console.ReadLine();
}
输出结果:

demo5:组合部件的嵌套
在导出部件中进行了导入操作
/// <summary>
/// 服务接口
/// </summary>
interface IGetString
{
void WriteString();
}
/// <summary>
///导出部件
/// </summary>
[Export("txt",typeof(IGetString))]
class GetString1 :IGetString
{
public void WriteString()
{
Console.WriteLine("Hello string1!");
}
}
/// <summary>
/// 导出部件
/// </summary>
[Export("db",typeof(IGetString))]
class GetString2 : IGetString
{
public void WriteString()
{
Console.WriteLine("Hello string2!");
}
}
/// <summary>
/// 导出部件导入了其他部件
/// </summary>
[Export]
class Getstring
{
[Import("txt",typeof(IGetString))]
public IGetString Txt { get; set; }
[Import("db", typeof(IGetString))]
public IGetString Db { get; set; }
}
宿主程序:
/// <summary>
/// 导入
/// </summary>
[Import]
public Getstring Service { get; set; }
/// <summary>
/// 组合
/// </summary>
void Compose()
{
var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catelog);
container.ComposeParts(this);
}
static void Main()
{
Program p = new Program();
p.Compose();
//根据最近一层的服务提供输出
p.Service.Txt.WriteString();
p.Service.Db.WriteString();
Console.ReadLine();
}
测试结果:

demo6:组合部件的延迟加载
部件准备工作
/// <summary>
/// 服务接口
/// </summary>
interface IGetString
{
void WriteString();
}
/// <summary>
///导出部件
/// </summary>
[Export("txt",typeof(IGetString))]
class GetString1 :IGetString
{
private string initTime;
public GetString1()
{
initTime = DateTime.Now.ToString("hh:mm:ss:");
}
public void WriteString()
{
Console.WriteLine("部件1初始化时间:\"{0}\"",initTime);
}
}
/// <summary>
/// 导出部件
/// </summary>
[Export("db",typeof(IGetString))]
class GetString2 : IGetString
{
private string initTime;
public GetString2()
{
initTime = DateTime.Now.ToString("hh:mm:ss:");
}
public void WriteString()
{
Console.WriteLine("部件2初始化时间:\"{0}\"", initTime);
}
}
导入
/// <summary>
/// 导入
/// </summary>
[Import("txt",typeof(IGetString))]
public IGetString Service1 { get; set; }
/// <summary>
/// 导出
/// </summary>
[Import("db", typeof(IGetString))]
public Lazy<IGetString> Service2 { get; set; }
宿主程序:
static void Main()
{
Program p = new Program();
//组合部件工作
p.Compose();
System.Threading.Thread.Sleep(2000);
p.Service1.WriteString();
//通过延迟加载,时间间隔为2秒
p.Service2.Value.WriteString();
Console.ReadLine();
}
输出效果:

demo7:组合部件的继承
导出部件,在接口中使用InheritedExport特性,在实现类中将省略Export标记
/// <summary>
/// 继承导出特性
/// </summary>
[InheritedExport]
interface IGetString
{
void WriteString();
}
/// <summary>
///继承导出功能
/// </summary>
class GetString :IGetString
{
public void WriteString()
{
Console.WriteLine("这是继承的导出部件");
}
}
宿主程序:
/// <summary>
/// 导入
/// </summary>
[Import]
public IGetString Service { get; set; }
/// <summary>
/// 组合
/// </summary>
void Compose()
{
var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catelog);
container.ComposeParts(this);
}
static void Main()
{
Program p = new Program();
//组合部件工作
p.Compose();
p.Service.WriteString();
Console.ReadLine();
}
输出效果:
组合容器(CompositionContainer)和目录(Catalog)
经过前面的demo练习,我们已经了解了MEF中的导入(Import)和导出(Export)。在本系列的第一篇文章中我们知道MEF其实还包括另外两个核心内容:组合容器(CompositionContainer)和目录(Catalog)。
在宿主程序中,我们需要通过组合容器和目录,将部件功能引入到当前的宿主程序应用中。
/// <summary>
/// 组合
/// </summary>
void Compose()
{
var catelog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catelog);
container.ComposeParts(this);
}
组合容器:比较常用的有CompositionContainer,而有时候会需要用到CompositionBatch,这里讲不做讲解。
目录:Assembly Catalog(程序集目录),Directory Catalog,Aggregate Catalog,Type Catalog,和仅使用在Silverlight中得目录Deployment Catalog( Silverlight only),Filtered Catalog.其中将着重讲解Assembly Catalog(程序集目录),Directory Catalog,Aggregate Catalog。
1.Assembly Catalog
可以在给定的Assembly 发现所有的导出部件,使用类型AssemblyCatalog。
2.Directory Catalog
它可以在给定的目录(路径,相对路径或绝对路径)中发现导出部件,使用类型DirectoryCatalog。如果你使用的是相对路径,则相对的是当前AppDoamin的基路径。DirectoryCatalog只会对给定目录进行一次性扫描,目录发生变化是容器不会主动刷新,如果需要刷新给定的目录需要调用方法:Refresh() ,当目录刷新时,容器也会重新组合部件。这个通常将一些动态链接库(.dll)作为部件导入到宿主程序中,可以灵活将DirectoryCatalog中的dll增加或移除,以对同一个API实现不同的应用。
demo8:使用Directory Catalog
在项目中开发导出部件:通过添加类库的方式,生成相应功能的.dll文件,其中需要遵照一个API标准(IGetString)。

1.通过MEFDemo8Service,定义API:

/// <summary>
/// 定义API,同时定义为继承导出方式
/// </summary>
[InheritedExport]
public interface IGetString
{
void OutPut();
}
2.通过MEFDemoPart1和Part2分别实现相应的接口

分别实现了部件1和部件2的方法
/// <summary>
/// 实现接口
/// </summary>
public class GetString:MEFDemo8Service.IGetString
{
public void OutPut()
{
Console.WriteLine("执行了部件1的方法");
}
}
3.在宿主程序中,添加API的引用以及添加分别生成part1和part2的.dll文件

其中Lib文件夹中的dll将属性设置为内容,并复制
在宿主程序中,通过导入,组合(使用DirectoryCatalog,指定Lib文件夹),再通过Main的执行,显示出具体的实现内容,代码如下:
class Program
{
/// <summary>
/// 导入部件
/// </summary>
[ImportMany(typeof(MEFDemo8Service.IGetString))]
public IEnumerable<MEFDemo8Service.IGetString> Service { get; set; }
//组合部件
void Compose()
{
var catelog = new DirectoryCatalog("Lib");
var container = new CompositionContainer(catelog);
container.ComposeParts(this);
}
static void Main()
{
Program p = new Program();
p.Compose();
foreach (var server in p.Service)
server.OutPut();
Console.ReadLine();
}
最终的执行效果:

当在Lib中移出了部件1

最终效果:

当部件添加到Lib中,又得到了最开始的效果。
由此可见,使用Lib,可以将我们的具体实现通过物理方式隔离,在需要的时候添加,不需要的时候移除即可。
3.Aggregate Catalog
聚集目录,有时候我们使用单一的Assembly Catalog和Directory Catalog并不能解决我们的需求,我们可能需要同时使用到他们,这时候我们便可使用Aggregate Catalog,我们可以将Assembly Catalog和Directory Catalog同时添加到目录中,这种添加可以通过构造函数实现,也可以通过目录集合的添加方法来实现:catalog.Catalogs.Add(...)。聚集目录使用类型AggregateCatalog。
MEF带来的联想
留给大家来回答吧!!!
,
MEF的学习笔记的更多相关文章
- C#可扩展编程之MEF学习笔记(五):MEF高级进阶
好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...
- C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻
前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...
- C#可扩展编程之MEF学习笔记(三):导出类的方法和属性
前面说完了导入和导出的几种方法,如果大家细心的话会注意到前面我们导出的都是类,那么方法和属性能不能导出呢???答案是肯定的,下面就来说下MEF是如何导出方法和属性的. 还是前面的代码,第二篇中已经提供 ...
- C#可扩展编程之MEF学习笔记(二):MEF的导出(Export)和导入(Import)
上一篇学习完了MEF的基础知识,编写了一个简单的DEMO,接下来接着上篇的内容继续学习,如果没有看过上一篇的内容, 请阅读:http://www.cnblogs.com/yunfeifei/p/392 ...
- C#可扩展编程之MEF学习笔记(一):MEF简介及简单的Demo
在文章开始之前,首先简单介绍一下什么是MEF,MEF,全称Managed Extensibility Framework(托管可扩展框架).单从名字我们不难发现:MEF是专门致力于解决扩展性问题的框架 ...
- Caliburn.Micro学习笔记(一)----引导类和命名匹配规则
Caliburn.Micro学习笔记目录 用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
随机推荐
- 利用反射+AOP,封装Basehandler
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点, ...
- FireDAC 连接access MDB数据库的方法
Use Cases Open the Microsoft Access database. DriverID=MSAcc Database=c:\mydata.mdb Open the Microso ...
- No identities are available for signing的解决方法
今天将做好的app提交到app store,结果就出现标题上的错误.“No identities are available for signing”.这个错误对于一个iOS开发者来说已经很平常了,这 ...
- git学习——git理解和仓库的创建
一.git用的3个工作的状态的理解. 1. 工作区 workspace(modified); 2. 暂存区 stage(staged) ; 3. git本地仓库 repository(commited ...
- 【Web】URI和URL,及URL的编码
URI和URL是什么,以及他们的区别 URL,Uniform Resource Locator,统一资源定位符.用于表示网络上服务器的资源所在位置,比如我们输入浏览器的地址. URI,Uniform ...
- 【转】如何查看当前Open的Cursor
遇到错误:A cursor with thename ' ' already exists,想要看是什么代码导致的.找到下面几种方法. --测试声明Cursor并且打开 DECLARE vend_cu ...
- The first DP!
P3399 丝绸之路 题目背景 张骞于公元前138年曾历尽艰险出使过西域.加强了汉朝与西域各国的友好往来.从那以后,一队队骆驼商队在这漫长的商贸大道上行进,他们越过崇山峻岭,将中国的先进技术带向中亚. ...
- 美国 ZIP Code 一览表
今天给大家提供美国的Zip Code的原因是大家在注册国外的账号时,需要提供这个Zip Code,因为一般美国的服务默认是面向美国的,甚至是仅支持美国. 以下提供一些美国的zip code 列表. 邮 ...
- Try to write a script to send e-mail but failed
#-*-coding: utf-8 -*- '''使用Python去发送邮件但是不成功,运行后,等待一段时间, 返回[Errno 10060] A connection attempt failed ...
- Openstack+Kubernetes+Docker微服务实践之路--Kubernetes
经过几番折腾终于搞定Kubernetes了,我们要在Openstack上部署Kubernetes集群,使用最新工具Kubeadm来安装,由于不能直接访问Kubernetes的源,我们需要一台可以穿墙的 ...