一、前言

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. Numpy使用Matplotlib实现可视化绘图

    Numpy使用Matplotlib实现可视化绘图 可以直接将Numpy的数组传给Matplotlib实现可视化绘图: 曲线图 饼图 柱状图 直方图 1. 绘制正弦曲线 2. 绘制饼图 3. 柱状图 4 ...

  2. poj_2386_dfs

    描述 由于最近的一场雨,农夫john的田地里很多地方流入了水,由一个N*M的矩形表示.每个方格要么有水(W)要么是干的(.).农夫想要知道他的田地里形成了多少池塘. 一个池塘由有水的方块相连,每个方块 ...

  3. 前端每日实战:134# 视频演示如何用 CSS 和 GSAP 创作一个树枝发芽的 loader

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/LJmpXZ 可交互视频 此视频是可 ...

  4. 【uniapp 开发】文字缩略css

    文字超出两行后显示省略号 display: -webkit-box; overflow: hidden; text-overflow: ellipsis; word-wrap: break-word; ...

  5. ABP源码分析 - 约定注册(2)

    比较随意,记录下过程,以便忘了以后重拾. 所谓约定注册是指不需要明确写代码注入,只需要按约定规则写服务类,框架自动完成服务注册. 例如,只要这样写,框架就会自动注册. public class Tax ...

  6. Mybatis-plugins分页助手实现查询数据分页

    其他具体代码接上文->mybatis自定义处理器 1.导入坐标 <dependency> <groupId>com.github.pagehelper</group ...

  7. C语言结构体指针与结构体变量作形参的区别

    区别 结构体变量 结构体变量作为形参,传递的是结构体变量本身,是一种值传递 形参结构体变量成员值的改变不影响对应的实参构体变量成员值的改变 结构体指针 结构体指针作为函数参数,传递的是指向结构体变量的 ...

  8. SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(上)

    不断踩坑并解决问题是每个程序员进阶到资深的必要经历并以此获得满足感,而不断阅读开源项目源码和总结思想是每个架构师成长最佳途径.本篇拉开SpringCloud Alibaba最新版本实战和原理序幕,以工 ...

  9. Codeforces Round #754 (Div. 2), problem: (A) A.M. Deviation泪目 万万没想到狂wa是因为这

    Problem - A - Codeforces 题目 题意很简单每次操作可以使得a1 a2  a3任意两个数分别+1  -1 求最后使得a+c-2b绝对值的最小值 BUG就是最后忽略了-2和2这一点 ...

  10. 比较爽的导航查询 功能 - SqlSugar ORM

    1.导航查询特点 作用:主要处理主对象里面有子对象这种层级关系查询 1.1 无外键开箱就用 其它ORM导航查询 需要 各种配置或者外键,而SqlSugar则开箱就用,无外键,只需配置特性和主键就能使用 ...