编程语言
首页 > 编程语言> > c# – 加载程序集和版本控制

c# – 加载程序集和版本控制

作者:互联网

我正在考虑通过提供一些预定义的接口来为现有应用程序添加一些可扩展性,这些接口可以通过在特定位置删除并由应用程序拾取的“插件”来实现.在更新和更频繁地部署插件时,应用程序的核心很少得到更新.

所以基本上,有这样的设置:

// in core assembly (core.dll)
public interface IReportProvider{
     string GenerateReport();
}

// in other assembly(plugin.dll)
public class AwesomeReport : IReportProvider {
     string GenerateReport(){ ... }
}

这两个项目都是相同构建过程的一部分,核心应用程序自行部署,插件将在稍后阶段放入.

我的问题是随着时间的推移汇编版本和解决方案.假设部署了core.dll v1,我想插入一个插件.如果plugin.dll引用core.dll v1,这很有用.但是,如果plugin.dll是针对core.dll的更高版本编译的(作为构建的一部分,比如v2),插件无法加载,因为它引用了core.dll v2,但部署的版本只有core.dll V1.

这是合理的和预期的行为,但它给了我这个项目设置方式的几个痛点,即插件的开发/更新不能仅通过再次运行构建并放入新插件(现在具有较新的版本依赖项).

(我知道解决较新程序集到旧程序集的潜在问题以及类型定义中可能存在的不匹配问题.问题仅在于修复高级程序集解决问题,而不是修复与不匹配类型定义相关的问题.)

我看到了几个让事情发挥作用的选择,其中没有一个像我想的那样简单:

>将绑定重定向添加到web.config,指示所有core.dll v1引用解析为core.dll v1引用
>创建包含接口定义的’contracts.dll’程序集,并在不同的构建中保持此特定程序集的版本号不变
>针对部署的core.dll版本构建插件(以某种方式在开发中引用已部署的版本)

如上所述,对我来说,这些都不是真正的低水果,我希望有人有更快的解决方案吗?

解决方法:

我在这个领域已经进行了相当广泛的工作,并且一直发现你需要在范围的内部和外部确定一些边界.想要最具可扩展性是一种风险,但这一切都需要付出代价.这个成本要么是编译时间,要么是约定,最佳实践,也许是自动构建检查 – 或者它将是一个复杂的运行时.

要解决您列出的选项:

>绑定重定向只是实现解决方案的部分工具.它们将允许你的程序“滑动”一个版本的DLL代替另一个版本,但是,它不会神奇地解决方法改变时发生的问题. MissingMethodException?
在这里,你可能没有的东西,也就是依赖链.

您可以看到,虽然应用程序将依赖项“A”视为版本1对象,但在内部它会从更高版本创建一些内容,该版本将被传递回应用程序并转换为v1.0 – 从而导致异常.
这可能很难处理 – 而且只是其中一个风险.
>在构建版本中保持合同汇编版本相同
这可以优雅地工作,但是,它只是将复杂性从构建时间延迟到运行时.
您需要努力确保您的更改不会破坏不同版本之间的兼容性.更不用说随着您的应用程序老化,您将在这些合同中收集许多您想要弃用的声明.最终,这个文件会变得庞大,繁琐,并且会让开发人员感到困惑 – 这甚至不能说明你拥有的所有实体合同!
>我不太清楚你的意思是什么,以及它如何涵盖你的问题空间.

您可以采取的另一种方法是为“SDK”的每个主要版本创建一个新合同.这具有一些政治优势,因为它在一段时间内修复了主要功能,这意味着我们可以将功能请求保持在合理的期望水平,除此之外的任何事情(需要新一代合同)都会延迟到下一个主要版本”.
然而,这确实需要在设计你的功能时勤勉尽责 – 用一些预先考虑的方式写下你的合同,这样你就可以预先考虑最明显的要求 – 但我真的觉得这无处不在……他们被称为’合同’因为某种原因.
每个新合同都将存在于版本命名空间(Company.Product.Contracts.v1_0,Company.Product.Contracts.v1_1)中.

我不会链接我的合同(每个新版本的合同都继承了最后一个).这样做会让您回到保持版本号相同的问题,在完全打破链条的情况下,您永远无法完全摆脱功能.

当您的插件加载时,它可以询问主机它支持的功能级别(合同版本) – 如果它是一个旧的主机/更新的插件场景:或者,编程插件以减少其运行时功能以处理较小的主机功能,或者干脆拒绝加载.
你可能应该正在执行这些检查,因为没有任何魔法允许你的插件利用主机中的功能而不是那里!微软的MAF框架试图通过使用垫片基础设施来实现这一目标,但它会导致大多数人的大量复杂性.

所以,你需要考虑的一些事情是:

>扩展您的可扩展性要求!您想要完成的一切都将花费您持续的维护.
>考虑一下如何弃用功能
>不要忘记你的合同将包含实体和逻辑合同,这些合同与逻辑合同的考虑略有不同,因为它们经常被传递得更多.
>仔细考虑是否在编译时更好地执行每个兼容性检查而不是运行时(反之亦然)
>版本编号!程序集版本号对于运行时行为很有用,文件版本号可以帮助您在版本控制中跟踪DLL返回源 – 同时使用它们!
>如果您正在进行自定义DLL解析,请使用app.config来定义自定义DLL位置,而不是程序集解析事件.配置方法更容易预测,嘿,它在易于阅读的Xm​​l中声明! Fusion Log Viewer还可以很好地报告DLL插入探测链的位置,而assembly-resolve事件将隐藏代码中的所有逻辑和规则.
唯一真正的缺点是使用app.config意味着更改将不会生效,直到您的应用程序重新读取配置文件(通常是应用程序重新启动),但如果您正在对您的插件进行AppDomain隔离,您甚至可以解决这个问题.

与你的’core.dll’问题有关……
我想,对你而言,这是一个相对简单的问题.您的Core合约DLL中的所有内容都应该存在于版本命名空间下(参见上文,Company.Product.v1_0等),因此您的DLL也包含版本号实际上是有意义的.这将消除DLL在部署到bin文件夹时相互覆盖的问题.
不要依赖于GAC – 从长远来看,这将是一个痛苦.尽管很遗憾,开发人员似乎总是忘记GAC会覆盖所有内容,这可能会成为调试的噩梦 – 它也会影响您在部署方面的权限.

如果你确实需要保持DLL名称相同 – 你可以在你的应用程序中创建一个’本地gac’,这将使你能够存储你的DLL,使他们不会相互覆盖,但仍然可以解决运行时.查看app.config(see my answer here)中的“绑定重定向”.这可以与应用程序的bin文件夹下的“伪GAC文件夹结构”结合使用.然后,您的应用程序将能够找到所需的任何DLL版本,而无需任何自定义程序集解析代码逻辑.
我将使用您的应用程序部署您之前支持的所有core.dll版本.
如果您的应用程序达到版本9,并且您已决定支持插件版本7和8,那么您只需要包含Core.7.dll,Core.8.dll和Core.9.dll.您的插件加载逻辑应检测旧版Core的依赖关系,并提醒用户该插件不兼容.

这个话题有很多,如果我想到与你的事业相关的任何事情,我会回来看看……

标签:c,plugins,net,versioning,assembly-resolution
来源: https://codeday.me/bug/20190630/1331934.html