装载程序集和版本 [英] Loading assemblies and versioning

查看:164
本文介绍了装载程序集和版本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我考虑通过提供可以通过插件来实现删除在特定位置和由该应用数拾预定义接口加入一些可扩展到现有的应用程序。应用程序的核心,而插件被更新和更频繁地部署很少被更新。

I'm contemplating adding some extensibility into an existing app by providing a few predefined interfaces that can be implemented by "plugins" dropped at a specific location and picked up by the app. The core of the application rarely gets updated while the plugins are updated and deployed more frequently.

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

So basically, having a setup like this:

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

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

这两个项目都相同的构建过程中的所有部分和核心应用被部署在自己的和该插件是在后一阶段滴入。

Both projects are all part of the same build process and the core app gets deployed on its own and the plugins are dropped in at a later stage.

我的问题是与集版本和分辨率随着时间的推移。比方说,core.dll V1部署,我想在一个插件下降。如果plugin.dll被引用core.dll V1这个伟大的工程。但是,如果是plugin.dll对core.dll更高版本编译(作为构建的一部分,说v2)的插件加载失败,因为它是引用core.dll V2,但部署的版本只有core.dll V1。

My issue is with assembly versioning and resolution over time. Let's say that core.dll v1 is deployed and I want to drop in a plugin. This works great if the plugin.dll is referencing core.dll v1. However, if the plugin.dll is compiled against a later version of the core.dll (as part of the build, say v2) the plugin fails to load since it is referencing core.dll v2 but the deployed version only has core.dll v1.

这是合理的和预期的行为,但它给了我一对夫妇painpoints在这个项目中已经设置方式,即开发/更新插件不能随便做通过再次运行构建,并在新的插件下降(现在有新版本的依赖)。

This is reasonable and expected behavior but it gives me a couple of painpoints in the way this project has been set up, namely that development/updates to plugins cannot just be done by running the build again and dropping in the new plugins (which now have newer version dependencies).

(我与解决新组件年长意识到潜在的问题组件和类型定义的潜在的不匹配,问题完全是在固定的高级别大会决议的问题,而不是在与不匹配类型定义固定的问题。)

(I'm aware of the potential issues with resolving newer assemblies to older assemblies and the potential mismatches in type definitions. The question is solely on fixing the high level assembly resolution issue, not in fixing issues related to mismatching type definitions.)

我看把事情一对夫妇的选择工作,其中没有一个是一样简单,我真的想:

I see a couple of options for getting things to work, none of which are as simple as I would really like:


  1. 添加绑定重定向到网络.config文件,指示所有core.dll V1 +引用来解决作为core.dll V1引用

  2. 创建contracts.dll装配包含接口定义,并保持在这个特定的程序集的版本号跨不变建立

  3. 对core.dll的部署的版本(在某种程度上引用开发部署的版本)

  4. 栋插件
  1. Adding binding-redirects to web.config, instructing all core.dll v1+ references to resolve as core.dll v1 references
  2. Creating a 'contracts.dll' assembly containing the interface definitions and keep the version number on this specific assembly unchanged across builds
  3. Building plugins against the deployed version of core.dll (somehow reference the deployed version in development)

正如前面提到的,这些都不是真正的低挂水果,我和我希望有人有niftier解决方案?

As mentioned, none of these are really low hanging fruits for me and I'm hoping that someone has a niftier solution?

推荐答案

我已经在这个领域相当广泛的合作,并一致发现,你需要放下一些界限究竟周围是什么,进出的范围。这是最想要的可扩展性风险,但是这一切是有代价的。这成本要么是编译时,在约定,最佳实践,或许自动构建检查的形式 - 或者这将是一个复杂的运行库

I have worked quite extensively in this space, and have consistently found that you need to put down some boundaries around exactly what is in, and out, of scope. It is a risk to want the most extensibility, however it all comes at a cost. That cost will either be compile-time, in the form of conventions, best practices, and perhaps automated build checks - or it will be a complicated runtime.

要解决你已经列出的选项:

To address the options that you have listed:


  1. 绑定重定向仅达成一个解决方案的部分工具。他们将让你的程序滑动代替另一个DLL的一个版本,但是,它不会魔术般地解决方法时,改变会发生什么问题。 MissingMethodException?这里
    的东西,你可能不会有藏汉虽然,是依赖链。

    你可以看到,当应用程序处理的依赖'A'为一个版本1对象,在内部则创建从该被传递回应用程序和投射到1.0的更高版本的东西 - 引起异常。
    这可能会非常棘手处理 - 而只是风险之一

  1. Binding redirects are only a partial tool to achieve a solution. They will allow your program to 'slip' one version of a DLL in place of another, however, it will not magically solve the problem of what happens when methods change. MissingMethodException? Something here that you may not have though of aswell, is the dependency chain. You can see that while the application is treating dependency 'A' as a version 1 object, internally it is creating something from a later version which is being passed back to the application and cast to v1.0 - causing an exception. This can be tricky to handle - and is just one of the risks.

保持合同的程序集版本相同的跨建立
这可以很好的工作,然而,它只是推迟从集结时间复杂度,到运行时。
你将需要勤奋,以确保您的更改不会打破跨版本兼容。更何况,作为应用程序的年龄,你会收集了很多声明这些合同,你将要弃用。最终,这个文件会变大,笨重,并为开发者困惑 - 这甚至没有占全部实体承包,你也一样。

Keeping the contracts assembly version the same across builds This can work elegantly, however, it is just deferring the complexity from build-time, to runtime. You will need to be diligent to ensure that your changes to not break compatibility across versions. Not to mention that as your application ages, you will collect a lot of declarations in these contracts that you will want to deprecate. Eventually this file will become large, cumbersome, and confusing for developers - and that is not even accounting for all the entity contracts you have as well!

我'!米也不太清楚,你通过这一个意思,以及它如何覆盖你的问题的空间。

I'm not too sure what you mean by this one, and how it covers your problem space.

这是另一种方法,你可以采取,我们做到了,是创造了SDK的每一个主要版本的新合同。这样做的,因为它解决的主要功能进行了一段时间,这意味着我们可以期待一个合理的水平保持功能要求一些政治优势,任何超出(需要新一代的合同)被推迟到下一个主要版本。
这确实,但是,需要在功能设计尽职调查 - 一些深谋远虑写你的合同,这样你就可以抢先最明显的要求 - 不过,我真的觉得这个不用说反正...他们是所谓的合同的一个原因。
每一个新的合同将存在于一个版本命名空间(Company.Product.Contracts.v1_0,Company.Product.Contracts.v1_1)。

An alternate approach you could take, which we did, is to create a new contract for each major release of the 'SDK'. This has some political advantages in that it fixes major functionality for a period of time, meaning that we can keep feature requests at a reasonable level of expectation, and anything beyond that (requiring a new generation of contract) is deferred until 'next major release'. This does, however, require diligence in the design of your functionalities - write your contracts with some forethought so you can pre-empt the most obvious requirements - however I really feel that this goes without saying anyway... they are called 'contracts' for a reason. Each new contract would exist in a version namespace (Company.Product.Contracts.v1_0, Company.Product.Contracts.v1_1).

我不会链条我的合同(合同继承过去的每个新版本)。这样做可以带你回到问题与保持版本号相同​​的,你永远无法完全得到不完全打破链条摆脱功能。

I would NOT chain my contracts (each new version of contract inheriting the last). Doing so takes you back to the issues with keeping the version number the same, you can never fully get rid of functionality without completely breaking the chain.

当你的插件加载,也可以请它支持什么级别的功能的主机(合约版) - 并且如果它的一个旧主机/更新插件的情况:要么,程序插件,以减少其运行时功能来处理较小的主机功能,或者干脆拒绝加载。
你可能应该无论如何执行这些检查,因为没有魔力,可以让你的插件中,根本不存在主机使用的功能!微软的MAF框架尝试使用垫片的基础设施来实现这一点,但它会导致对大多​​数人复杂的巨量。

When your plugin loads, it can ask the host what level of functionality it supports (contract version) - and if its an old host/newer plugin scenario: either, program the plugin to reduce its runtime functionality to deal with the lesser host capabilities, or simply refuse to load. You should probably be performing these checks anyway, because there is no magic that will allow your plugin to utilise functionality in the host that simply isn't there! Microsoft's MAF framework attempts to achieve this by using a shim infrastructure, but it leads to a massive amount of complexity for most people.

所以,有些事情你需要考虑主要有:

So, some things you will need to consider are:


  1. 的范围扩展的需求!你想完成的一切将花费您在日常维护。

  2. 想想你将如何去贬低功能

  3. 请不要忘记,你的合同将包含实体和逻辑的合同,这些都略有不同的考虑因素为逻辑的合同,因为他们往往​​通过周围更多。

  4. 仔细考虑一下,如果每个兼容性检查将在编译时表现较好而不是运行时(反之亦然)

  5. 版本编号!大会的版本号是伟大的运行时行为,文件版本号是有帮助你跟踪一个DLL回到源版本控制 - 利用两者

  6. 如果你的!做自定义DLL的分辨率,使用的app.config来定义您的自定义DLL的位置,而不是组装解决事件。该配置的做法是更具可预测性,而且,嘿,这在轻松可读的XML的声明!融合日志查看器也将很好地报告在哪里探测链中的DLL插入,而组装解决事件将隐藏在代码中所有的逻辑和规则。
    中的唯一真正的缺点是使用的app.config装置改变不会生效,直到您的应用程序重新读取配置文件(通常是应用程序重新启动),但如果你正在做你的插件AppDomain隔离,您甚至可以解决这个问题。

  1. Scope your extensibility requirements! Everything you want to accomplish will cost you in ongoing maintenance.
  2. Think about how you will go about deprecating functionality
  3. Don't forget that your contracts will contain entities as well as logic contracts, these have slightly different considerations to logic contracts because they are often passed around much more.
  4. Carefully consider if each compatibility check would be better performed at compile time instead of run-time (or vice-versa)
  5. Version numbering! Assembly version numbers are great for runtime behavior, file version numbers are there to help you track a DLL back to the source in your version control - make use of both of them!
  6. If you are doing custom DLL resolution, use the app.config to define your custom DLL locations, NOT the assembly resolve events. The config approach is much more predictable, and, hey, it's declared in easily readable Xml! The Fusion Log Viewer will also nicely report where in the probing chain your DLL was inserted, whereas the assembly-resolve event will hide all your logic and rules in code. The only real downside is that using the app.config means changes wont take effect until your application re-reads the config file (typically app restart), but if you are doing AppDomain isolation of your plugins, you can even get around this.

有关您'core.dll问题...
我想,对你来说,这是一个相对简单的问题。在你的核心合同DLL凡事都要下一个版本的命名空间中(见上文Company.Product.v1_0等),因此它实际上是有道理的,你的DLL还包含一个版本号。这将删除你的DLL时,部署到bin文件夹互相覆盖的问题。
DONT依靠GAC - 这将是从长远来看,有疼痛感。尽可能多的一个耻辱,因为它是,开发商似乎总是忘记了GAC压倒一切,而这可以成为一个调试的噩梦 - 它也将与问候权限影响您的部署方案。

Pertaining to your 'core.dll' problem... I think, for you, this is a relatively simple problem. Everything in your Core contracts DLL should exist under a version namespace (see above, Company.Product.v1_0 etc), so it actually makes sense that your DLL also contain a version number. This will remove the problem of your DLLs overwriting each other when deployed to the bin folder. DONT RELY ON THE GAC - this will be a PAIN in the long run. As much of a shame as it is, developers always seem to forget that the GAC overrides everything, and this can become a debugging nightmare - it would also influence your deployment scenarios with regards to permissions.

如果您确实需要保留DLL的名称一样 - 你可以创建你的应用程序中的本地GAC,这将使你的DLL文件存放在这样一种方式,他们不覆写对方,但都还运行时解析。看看在App.config绑定重定向'(的在这里看到我的回答)。这可以通过您的应用程序的bin文件夹下的一个伪GAC的文件夹结构相结合。然后,您的应用程序将能够找到没有任何自定义的汇编代码解决逻辑所需的DLL的任何版本。
我会部署core.dll所有支持的先前版本,您的应用程序。
。如果你的应用程序获取到9版本,并且你已经决定支持7版本和插件的8为好,那么你只需要包括Core.7.dll,Core.8.dll和Core.9.dll 。你的插件加载逻辑应该检测到旧版本核心的依赖,并提醒用户该插件不兼容。

If you do need to keep the DLL name the same - you can create a 'local gac' within your application which will enable you to store your DLLs in such a way that they dont over-write each other, but are all still resolvable by the runtime. Check out 'binding redirect' in the app.config (see my answer here). This can be combined with a 'pseudo GAC folder structure' under your application's bin-folder. Your app will then be able to locate any version of DLL required without any custom assembly-resolving code-logic. I would deploy all previously supported versions of your core.dll, with your application. If your application gets to version 9, and you have decided to support versions 7 and 8 of plugins as well, then you only need to include Core.7.dll, Core.8.dll and Core.9.dll. Your plugin loading logic should detect dependencies on older versions of Core, and alert the user that the plugin is not compatible.

有很多这个话题,如果我想别的就是有关为您的事业,我会回来检查...

There is a lot to this topic, if I think of anything else that is pertinent to your cause, I'll check back...

这篇关于装载程序集和版本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆