实战MEF(1):一种不错的扩展方式
在过去,我们完成一套应用程序后,如果后面对其功能进行了扩展或修整,往往需要重新编译代码生成新的应用程序,然后再覆盖原来的程序。这样的扩展方式对于较小的或者不经常扩展和更新的应用程序来说是可以接受的,而对于像ERP系统那样复杂而且常常需要扩展的应用程序,这种扩展方法就不够方便,因为每次都要修改源代码或重新引用组件。
尤其是组件(许多dll),如果每编写一个新组件又要在主项目中引用一次,显然主项目就不得不经常重新生成。要是能有一种机制,可以在主项目应用程序不作任何修改就可以自动识别并扩展组件,就会很便捷,我们每次扩展只需要更新或者添加某些dll文件即可。
MEF正是为了解决上述问题而诞生。MEF全称Managed Extensibility Framework,至于如何翻译不重要,你喜欢怎么个译法都无所谓,我们只要明白它用来干啥就好了。
宽泛的理论似乎作用不明显,我们还是先来弄一个简单的例子。现在假设我在开发一个应用程序,首先我要为一些组件以及将来可以要扩展的组件定义公共接口(或者说是协定,大家是否记得在WCF中也是这样,先定义一些公共的服务协定,然后视具体情况对这些协定进行扩展),然后我可以按照不同的情形去实现这些接口,这也是我们常说的,接口可以起到规范作用,有了规范,正是为后期扩展打下可行性基础。
例子的主项目是一个控制台应用程序,我们先在解决方案中添加一个类库项目,为了简单演示,我定义了以下接口:
public interface IExtBase
{
void DoTask();
string TaskName { get; }
}
这个IExtBase接口就作为我们要扩展的组件的公共协定,不管我以后怎么扩展,哪怕我要添加100000个组件,这些组件都要实现IExtBase接口。
这里我做了两个扩展作为例子,为了表明MEF框架能自动发现组件,我把两个实现IExtBase接口的类写到另外一个类库项目中——TaskToa.dll。
[Export("task1", typeof(CommExtBase.IExtBase))]
public class Task_1 : CommExtBase.IExtBase
{
public void DoTask()
{
Console.WriteLine("任务1执行。");
}
public string TaskName
{
get
{
return "任务1";
}
}
}
[Export("task2", typeof(CommExtBase.IExtBase))]
public class Task_2 : CommExtBase.IExtBase
{
public void DoTask()
{
Console.WriteLine("任务2执行。");
}
public string TaskName
{
get { return "任务2"; }
}
}
附加ExportAttribute特性用于扩展的组件类,表示它们将被导出,导出的类型会被MEF自动发现。
在主项目中我们不引用这个TaskToa类库,先把TaskToa项目生成一个TaskToa.dll,直接复制到.exe应用程序的动行目录下,在调试模中为\\项目\\bin\\Debug目录下。
由于实现公共接口的类不止一个,后续可能还有10000000个,为了能够使所有的扩展组件都能被发现,统一的协定类型为IExtBase接口(与WCF的实现服务协定相似),在附加ExportAttribute特性时指定了每个组件类的协定名,而协定类型都是IExtBase接口,协定类型必须统一才能保证所有扩展的类能被MEF框架发现。
最后在.exe主项目的代码中加入以下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace MainApp
{
class TestWork
{
[Import("task1")]
public CommExtBase.IExtBase Task1;
[Import("task2")]
public CommExtBase.IExtBase Task2;
}
class Program
{
static void Main(string[] args)
{
ApplicationCatalog appCat = new ApplicationCatalog();
CompositionContainer container = new CompositionContainer(appCat);
TestWork tw = new TestWork();
try
{
container.ComposeParts(tw);
Console.WriteLine("Task1的类型:{0}\tTaskName: {1}\t调用DoTask方法:",tw.Task1.GetType().Name,tw.Task1.TaskName);
tw.Task1.DoTask();
Console.Write("\n\n");
Console.WriteLine("Task2的类型:{0}\tTaskName:{1}\t调用DoTask方法:",
tw.Task2.GetType().Name, tw.Task2.TaskName);
tw.Task2.DoTask();
}
catch (CompositionException cex)
{
Console.WriteLine(cex.Message);
}
Console.Read();
}
}
}
TestWork类用来包装最后被合并的组件,它有两个公共字段,类型虽然都是IExtBase,但由于应用了ImportAttribute特性,并且指定了协定名,这些协定名一定要与我们之前在扩展类中应用ExportAttribute是指定的协定名相对应。附加了ImportAttribute特性可以让MEF识别对应的组件并导入到TestWork类中。
在Main入口点中,我们先使用ApplicationCatalog类来收集所有可用的扩展组件,然后把收集到的信息传给CompositionContainer容器,容器负责把收集到的组件进行合并(组装)。合并完成后我们就可以使用这些组件了。
本例子的运行结果如下面的截图所示:

从截图中我们看到,TestWork类的Task1和Task2字段的类型分别为Task_1和Task_2,同时也调用了它们的成员,输出结果表明,我们之前开发的两个扩展类Task_1和Task_2已经自动导入到我们当前的应用程序中了。
ApplicationCatalog类是在当前应用程序的运行目录下查找所有符合要求的exe或dll中的扩展组件,一旦找到就自动收集并生成组件目录,而后提供给CompositionContainer进行组装。
从这个例子我们看到MEF框架就像一个大型的组装厂车间,首先设计师们寻找灵感,构思产品的基本模型,这也就是我们所定义的公共接口规范;随后,进行精确计算,进一步把抽象的模型变为具体的工程图,这相当于我们自己实现编写的各个扩展类;接着,相关工作人员会把设计师和工程师做好的各个零部件的工程图收集整理,准备提供给车间进行生产组装,这就相当于我们例子中的ComposablePartCatalog,我们例子中用到的ApplicationCatalog只是其中一个收集方式,其他的方式还有按程序集进行收集或按特定路径目录下的所有类库进行收集。然后车间开始制作并组装成产品,最终投入使用。
我们可以用下面的图来描述一下整个过程(此图纯属虚构,如有雷同,实属巧合)

现在我们先不必过多关注代码细节,因为后面我会慢慢介绍,我们只要明白MEF的用途就可以了。
OK,本文就说到这里吧,88
实战MEF(1):一种不错的扩展方式的更多相关文章
- 实战MEF(1)一种不错的扩展方式
在过去,我们完成一套应用程序后,如果后面对其功能进行了扩展或修整,往往需要重新编译代码生成新的应用程序,然后再覆盖原来的程序.这样的扩展方式对于较小的或者不经常扩展和更新的应用程序来说是可以接受的,而 ...
- Jenkins持续集成企业实战系列之两种网站部署的流程-----01
注:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任. 最初接触Jenkins也是由于公司需求,根据公司需求Java代码项目升级的.(公司是 ...
- 两种不同的扩展Scrum的方式
两种不同的扩展Scrum的方式 1.LeSS和LeSS Huge –大型Scrum LeSS(和LeSS Huge –真正的大型程序)的合著者Craig Larman首先批评了管理,开发人员和客户传统 ...
- 实战MEF(4):搜索范围
在前面的文章中,几乎每个示例我们都会接触到扩展类的搜索位置,我们也不妨想一下,既然是自动扩展,它肯定会有一个或者多人可供查找的位置,不然MEF框架怎么知道哪里有扩展组件呢? 就像我们用导航系统去查找某 ...
- 实战MEF(2):导出&导入
上一文中,我们大致明白了,利用MEF框架实现自动扫描并组装扩展组件的思路.本文我们继续前进,从最初的定义公共接口开始,一步步学会如何使用MEF. 在上一文中我们知道,对于每一个实现了公共规范的扩展组件 ...
- jQuery插件主要有两种扩展方式
jQuery插件主要有两种扩展方式: 扩展全局函数方式. 扩展对象方法方式. 扩展全局函数方式 扩展全局函数方式定义的插件,即类级别插件,可以通过jQuery.extend()来进行定义.定义格式为: ...
- Yii实战中8个必备常用的扩展,模块和widget
Yii实战中8个必备常用的扩展,模块和widget 在经过畅K网 的实战后,总结一下在Yii的项目中会经常用到的组件和一些基本的使用方法,分享给大家,同时也给自己留个备忘录,下面我以代码加图片说明. ...
- 三种Tomcat集群方式的优缺点分析
三种Tomcat集群方式的优缺点分析 2009-09-01 10:00 kit_lo kit_lo的博客 字号:T | T 本文对三种Tomcat集群方式的优缺点进行了分析.三种集群方式分别是:使用D ...
- http 3种web会话管理方式
http是无状态的,一次请求结束,连接断开,下次服务器再收到请求,它就不知道这个请求是哪个用户发过来的.当然它知道是哪个客户端地址发过来的,但是对于我们的应用来说,我们是靠用户来管理,而不是靠客户端. ...
随机推荐
- 一致性hash算法详解
转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT) ...
- paramiko模块的安装
1.找到自己python安装的目录(默认路径:C:\Users\zhangliyuan\AppData\Local\Programs\Python\Python35) 注:cmd中所有命令 2.进入S ...
- Django 中related_name,"%(app_label)s_%(class)s_related"
先看个model from django.db import models # Create your models here. class Parent(models.Model): name = ...
- Unity3D 导航网格自动寻路(Navigation Mesh)
NavMesh(导航网格)是3D游戏世界中用于实现动态物体自动寻路的一种技术,将游戏中复杂的结构组织关系简化为带有一定信息的网格,在这些网格的基础上通过一系列的计算来实现自动寻路..导航时,只需要给导 ...
- toroiseSVN 无法连接服务器,提示unable connect to ……url 参数错误
之前使用的好好的,有天突然提示无法连接repository url,能ping通服务器,就是一直报错,找了很多方法,如: 1.删除缓存及缓存文件 2.删除软件并重新安装 3.关闭windows防火墙 ...
- 流行的JavaScript库 ——jQuery
1.为了简化 JavaScript 的开发, 一些 JavsScript 库诞生了. JavaScript 库封装了很多预定义的对象和实用函数.能帮助使用者建立有高难度交互的 Web2.0 特性的富客 ...
- SQL Server 2008, 2008 R2, 2012 and 2014 完全支持TLS1.2加密传输
SQL Server 2008, 2008 R2, 2012 and 2014 完全支持TLS1.2加密传输 微软高兴地宣布所有主流SQL Server客户端驱动和SQL Server发行版已经支持T ...
- 最近碰到的一些 SSL 问题记录
最近碰到一些 SSL 的小问题,特记录下. 我们有个 Java 实现的 SSL TCP 服务端,为客户端(PC.Android 和 iOS)提供 SSL 接入连接服务.最近有用户反馈其手机上 App ...
- Python黑帽编程1.2 基于VS Code构建Python开发环境
Python黑帽编程1.2 基于VS Code构建Python开发环境 0.1 本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks Atta ...
- 剑指Offer面试题:20.栈的压入、弹出序列
一.题目:栈的压入.弹出序列 题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1.2.3.4.5是某栈的压栈序列,序列4 ...