一、前言

MEF(Managed Extensibility Framework),是轻量级的插件框架。使用简单,功能强大。详细介绍见MSDN,本文不再赘述。

在使用MEF时,会遇到这样一种场景:

主程序和插件都引用了同一个DLL中同一个【方法F】,但是引用的DLL版本不一致。

那么,程序在运行时,会出现4种情况:

(注:文字描述不太直观,可参照下节的实际演示)

1,不同版本DLL中【方法F】未做改变:插件可正常调用【方法F】。

2,不同版本DLL中【方法F】内部实现做了改变:引用了与主程序所引用的DLL版本不一致的插件,在调用【方法F】时,调用的不是插件所引用的DLL版本中的【方法F】,而是调用的主程序所引用的DLL版本中的【方法F】。

3,不同版本DLL中【方法F】增加了重载方法:如果主程序所引用的DLL版本是包含重载方法的(即主程序所引用的DLL版本比插件引用的DLL版本),那么插件都可以正常调用,不过调用的【方法F】来自主程序所引用的DLL版本中的【方法F】;相反,如果主程序所引用的DLL版本是未包含重载方法的(即主程序所引用的DLL版本比插件引用的DLL版本),那么,那些调用了【重载方法F】的插件在运行时将会报错。

4,不同版本DLL中【方法F】发生了改变——增减参数、改变返回值类型、删除了方法等:引用了与主程序所引用的DLL版本不一致的插件,在调用【方法F】时会报错。

本篇文章,就是来讲一下如何实现主程序与插件们各自调用各自版本的DLL,互不影响且正常调用的。

相信看完的你,一定会有所收获!

本文地址:https://www.cnblogs.com/lesliexin/p/16280161.html


二、问题复现

(一)代码结构

为了方便演示,我们创建一个简单的MEF程序,其结构如下:

其中:

1,接口

接口类很简单,包含一个接口定义和一个自定义的MEF导出特性标签。

a,接口定义中,只包含了一个方法:Run();

b,自定义的MEF导出特性标签,主要是为MEF插件添加一个标记,方便对应到具体插件。

2,公共DLL

公共DLL类,即复现场景时,主程序和插件们都需要引用的类。

因为要生成不同版本的公共DLL,所以我们依次修改代码,并在生成属性中设置版本号,然后编译生成DLL。

依次修改4次,共计4个版本的公共DLL:v1.0、v2.0、v3.0、v4.0,其代码修改如下:

v1.0

v2.0

v3.0

v4.0

3,插件

这些插件除了引用的公共DLL版本不一致外,基本方法都是一样的:继承并实现接口。

这里由于“公共DLL v3.0”中对方法进行了重载,所以我们这里用两个插件来分别调用每一方法。

插件1”代码:

插件2”代码:

插件3”代码:

插件4”代码:

插件5”代码:

4,主程序

主程序的界面设计如下:

其中:

“主程序”按钮作用:直接调用公共DLL中的方法。

“插件1” - “插件4” 按钮作用:调用插件中的方法。

因为要复现场景,所以主程序也需要生成多个版本。

又因为在公共DLL v3.0中重载了方法,所以多分一个版本,来分别调用这两个方法。

所以最终会生成5个版本的主程序。

最终生成的文件结构如下:

主程序的代码,主要分为3部分:

3.1,加载MEF插件

在程序启动时,我们需要加载所有插件。

3.2,调用插件方法

因为所有的插件都是基于统一的接口,所以我们先写一个通用的调用插件方法,然后在点击按钮时,直接传入插件对应导出标记即可。

3.3,主程序调用公共DLL方法

对公共DLL的方法调用与插件并无二致,5个版本的主程序,其代码变化如下:

v1.0

v2.0

v3.0

v4.0

v5.0

(二)演示

我们依次编译生成不同版本的主程序,然后依次运行,其结果如下:

v1.0

v2.0

v3.0

v4.0

v5.0

可以发现,当月主程序与插件都引用相同的公共DLL后,无论插件引用的公共DLL版本是多少,调用的均是主程序所引用的公共DLL版本。

当插件与主程序引用的公共DLL中方法发生改变(如增减参数、修改返回值、删除了方法等),插件将会调用失败,抛出异常。


三、解决方案

问题已复现,那么我们该如何解决呢?

解决目标就是主程序与插件们,各自调用各自所引用版本的“公共DLL”。在本示例中,就是:插件1引用“公共DLL v1.0”,插件2引用“公共DLL v2.0”,等等。

而解决方案非常之简单,简单到一句话就能说完:

为公共DLL添加强签名

关于“强签名”,本文不再赘述,请参考MSDN。

下面,我们来对示例进行修改。

(一)添加强签名

我们在公共DLL上右键,选择“属性”,然后选择“签名”,按提示添加强签名即可。

(二)重新生成插件和主程序。

我们依次重新生成插件,和主程序,过程不再赘述。

(三)演示

我们重新依次运行5个版本的主程序,会发现问题已解决,主程序与插件们都各自调用了自己所引用版本的公共DLL。

其与未进行强签名时的运行结果对比如下:

v1.0

v2.0

v3.0

v4.0

v5.0


四、总结

说实话,本篇文章所描述的问题,虽然不难,解决办法也很简单,却曾经困扰了我好久。

之前一直没有找到现成的可行解决方案,曾在博问上提问过,答案虽然有用,但无奈不知怎么去应用。这事也就放下了。

最近在看《CLR via C#》,曾经看过,但走马观花、不知所云、无甚收获。此次重看,发现已可以读懂,颇有感获。

在看到了强签名时,蓦然发现,这不就是曾经困扰我很久的解决方法吗?

既心动便行动,经过一番测试,果然可行。

挺感慨的,果然往基础的方向去学习,是正确的。

本人水平有限,难免有所疏漏,欢迎各位读者评论指正。


五、源代码下载

https://files.cnblogs.com/files/lesliexin/MEFDemo.zip


-【END】-

(原创)[C#] MEF 主程序与插件加载不同版本的DLL的更多相关文章

  1. 基于.NET MVC的高性能IOC插件化架构(二)之插件加载原理

    上一篇博文简单介绍了下插件化的代码组成部分:http://www.cnblogs.com/gengzhe/p/4390932.html,源码地址:https://github.com/luohuazh ...

  2. Qt中如何 编写插件 加载插件 卸载插件

    Qt中如何 编写插件 加载插件 卸载插件是本文要介绍的内容.Qt提供了一个类QPluginLoader来加载静态库和动态库,在Qt中,Qt把动态库和静态库都看成是一个插件,使用QPluginLoade ...

  3. 纸壳CMS的插件加载机制

    纸壳CMS是一个开源的可视化设计CMS,通过拖拽,在线编辑的方式来创建网站. GitHub https://github.com/SeriaWei/ZKEACMS.Core 欢迎Star,Fork,发 ...

  4. Bootstrap 按钮(Button)插件加载状态

    通过按钮(Button)插件,您可以添加进一些交互.比如控制按钮的状态.或者为其它组件(工具栏)创建按钮组. 加载状态 如需向按钮添加加载状态,只需要简单地向 button 元素添加 data-loa ...

  5. Blender插件加载研究

    目标 [x] 解析Blender插件代码加载原理, 为测试做准备 结论 采用方法3的方式, 可以在测试中保证重新加载子模块, 是想要的方式, 代码如下: _qk_locals = locals() d ...

  6. Windows2003系统问题:“无法加载安装程序库wbemupgd.dll,或是找不到函数OcEntry.

    “无法加载安装程序库wbemupgd.dll,或是找不到函数OcEntry.请与您的系统管理员联系.特定错误码是 0x7e;" 然后是警告框: " 无法初始化应用程序." ...

  7. 提示“应用程序无法启动,因为应用程序的并行配置不正确”不能加载 System.Data.SQLite.dll

    新版本SQLITE,如果下载Precompiled Binaries版会出现提示“应用程序无法启动,因为应用程序的并行配置不正确”不能加载 System.Data.SQLite.dll. 下载Prec ...

  8. phonegap插件加载与使用

    有朋友问能不能在CanTK和AppBuilder开发的APP里发送UDP数据,HTML5里只能用HTTPS/HTTP/WebSocket几种通讯方式,要使用UDP需要通过phonegap打包成APK等 ...

  9. Eclipse 插件安装方法和插件加载失败解决办法

    一:是利用Eclipse Software  Update 添加网址,让Eclipse 自动的搜索下载最新的插件. 比如安装VE这个可视化编辑UI的插件,其步骤为 Help > Software ...

随机推荐

  1. C语言之常量(知识点4)

    一.常量(概念) ①用标识符代表常量 ②一般用大写字母表示 二.定义格式 #define 符号常量 常量 三.案例 #define PI 40; #define PRICE 30; 四.注意 ①其值在 ...

  2. 如何解决用response输出字符流数据时的乱码问题

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOE ...

  3. Streamlit:快速数据可视化界面工具

    目录 Streamlit简介 Streamlit使用指南 常用命令 显示文本 显示数据 显示图表 显示媒体 交互组件 侧边栏 缓存机制 Streamlit使用Hack Streamlit的替代品 相关 ...

  4. ubuntu sublime text3 python 配置 sublime text3 python 配置

    ubuntu sublime text3 python 配置     1.安装sublime text 3 安装过程非常简单,在terminal中输入: sudo add-apt-repository ...

  5. carsim输入输出变量

    来自:https://wenku.baidu.com/view/3405ded5443610661ed9ad51f01dc281e43a5673.html 输出量

  6. 左手Cookie“小甜饼”,右手Web Storage

    目录 1. Web Storage 2. Cookie机制 3. 二者的联系与区别 1.Web Storage 1.1 概述 Web Storage是HTML5提供的一种新的浏览器端数据储存机制,它提 ...

  7. 手动封装一个node命令集工具

    了解NPM安装模块时与项目配置文件中的bin配置发生了什么 了解nodejs在控制台中的运行环境及上下文 基于自定义命令集工具集成Yeoman 一.NPM模块安装内幕与nodejs控制台运行环境 1. ...

  8. docker中mysql导入sql文件

    1.先将文件导入到容器 docker cp **.sql [容器名]:/root/ 2.进入容器 docker exec -ti [容器名/ID]/bin/bash 3.将文件导入数据库 mysql ...

  9. i.MX rt 系列微控制器的学习记录

    杂记 前言 我总是很希望自己能产生一种感知电压变化的能力,就像B站上的教学动图中,电流从电源流出时导线就像LED亮起来一样,我将指尖触到导线上就能感受到实时的电压变化.我在上学和工作时经常由于无法理解 ...

  10. signed integer overflow整数溢出

    整数越界情况 1. 数组下标越界, 大于N或者小于0 2. 数字过大,可以选择取个模,或者换long long, double  我笑了 还有一个暂时没有好的解决方法的:string s:cin/输入 ...