2012-08-29 58 views
4

我正在考虑通过提供一些预定义的接口来增加一些现有应用程序的可扩展性,这些接口可以通过在特定位置放置并由应用程序拾取的“插件”实现。应用程序的核心很少更新,而插件更新和部署更频繁。加载程序集和版本

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

// 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。

这是合理的和预期的行为,但它给了我这个项目已经建立的方式几个痛点,即开发/更新插件不能通过再次运行构建和放入新的插件(现在有更新的版本依赖关系)。我知道解决较旧的程序集到较旧的程序集中的潜在问题以及类型定义中潜在的不匹配问题仅仅在于解决高级别程序集解决问题,而不是解决与不匹配类型定义相关的问题。)

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

  1. 添加绑定重定向到web.config中,指示所有core.dll v1 +参考解析为core.dll v1引用
  2. 创建“contracts.dll”组件包含的接口定义,并保持在这个特定的程序集跨越不变的版本号建立
  3. 对core.dll的部署版本构建插件(不知引用在发展部署版本)

如上所述,这些都不是真正的低挂水果对我来说,我希望有人有一个更加困难的解决方案?

+0

没有什么神奇的解决方案版本而这种DLL地狱。 CLR已经有了很好的保护,规避它应该是最后要考虑的事情。改为关注可靠的部署方案。 –

+1

为什么“Contracts.dll”不简单?我认为这是一个很好的解决方案。合同库永远不会改变,core.dll和plugin.dll将用它作为通信的“中间件”。 –

回答

0

无论你的理由可能是为了避免你的3分(我认为它更可靠和易于使用),你可以利用Assembly Resolve Event

包括代码,例如尝试加载你的插件前

static Assembly AppDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
     { 
      //replace the if check with more reliable check such as matching the public key as well 
      if (args.Name.Contains(Assembly.GetExecutingAssembly().GetName().Name)) 
      { 
       Console.WriteLine("Resolved " + args.Name + " as " + Assembly.GetExecutingAssembly().GetName()); 
       return Assembly.GetExecutingAssembly(); 
      } 

      return null; 
     } 

绑定上述处理程序按照您的core.dll。如果您在当前AppDomain中加载插件,然后将其绑定到当前域的AssemblyResolve

例如,

[SecurityPermission(SecurityAction.Demand, ControlAppDomain = true)] 
public static void LoadPlugins() 
{ 
    AppDomain.CurrentDomain.AssemblyResolve += AppDomain_AssemblyResolve; 
    Assembly pluginAssembly = AppDomain.CurrentDomain.Load("MyPlugin"); 
} 
3

我在这个领域有相当广泛的工作,并且一直发现你需要在范围内确定内外界限。希望获得最大的可扩展性是一种风险,但这一切都需要付出代价。这种成本要么是编译时间,要么以约定,最佳实践以及可能的自动化构建检查的形式 - 否则将是一个复杂的运行时。

为了解决您所列出的选项:

  1. 绑定重定向仅达成一个解决方案的部分工具。他们将允许你的程序'滑'一个DLL的版本来代替另一个版本,但是,它不会奇迹般地解决方法改变时会发生什么问题。 MissingMethodException? 在这里,你可能没有的东西是依赖链。 dependency chains 您可以看到,尽管应用程序将依赖关系“A”视为版本1对象,但它在内部正在从正在传回应用程序并转换为v1.0的更高版本创建一些内容,并导致出现异常。 这可能很难处理 - 而且只是其中一个风险。

  2. 保持合约程序集版本在整个版本中保持一致 这可以很好地工作,但它只是将构建时间的复杂性推迟到运行时。 您需要努力确保您的更改不会破坏版本之间的兼容性。更不用说,随着您的申请年龄的增长,您将在这些合同中收集大量您希望弃用的声明。最终,这个文件将变得庞大,繁琐,并且让开发人员感到困惑 - 而且这甚至不能说明你拥有的所有实体合同!

  3. 我不太清楚你这个意思是什么,它是如何覆盖你的问题空间的。

您可以采取的另一种方法是我们所做的,就是为'SDK'的每个主要版本创建一个新合同。这具有一些政治优势,因为它在一段时间内修复了主要功能,这意味着我们可以将功能请求保持在合理的期望水平,并且除此之外的任何内容(需要新一代合同)被推迟到下一个主要版本”。然而,这确实需要努力设计你的功能 - 用一些预先考虑的方式来编写你的合同,这样你就可以抢占最显而易见的要求 - 但是我真的觉得这是不言而喻的......他们被称为'合同' 因为某种原因。 每个新合同都将存在于版本名称空间(Company.Product.Contracts.v1_0,Company.Product.Contracts.v1_1)中。

我不会链接我的合同(合同的每个新版本继承最后一个)。这样做会让您回想起保持版本号相同的问题,您将无法完全摆脱功能而不会完全破坏链条。

当你的插件加载时,它可以询问主机它支持什么级别的功能(合同版本) - 如果它是一个旧的主机/较新的插件场景:要么编程插件以减少它的运行时功能来处理较低的主机功能,或者只是拒绝加载。 无论如何,你应该正在执行这些检查,因为没有魔法可以让你的插件利用主机中的功能,而这些功能根本就不存在!微软的MAF框架试图通过使用垫片基础设施来实现这一目标,但是这对大多数人来说却导致了大量的复杂性。

所以,有些事情你必须要考虑的是:

  1. 范围扩展你的要求!你想要完成的任何事情都会让你花费在持续的维护中。
  2. 想想你将如何去掉功能
  3. 不要忘记你的合约将包含实体和逻辑合约,这些对逻辑合约的考虑略有不同,因为它们经常被传递得更多。
  4. 仔细考虑每个兼容性检查是否在编译时而不是运行时更好地执行(反之亦然)
  5. 版本编号!汇编版本号非常适合运行时行为,文件版本号可帮助您将DLL追溯回版本控制中的源代码 - 充分利用它们!
  6. 如果您正在执行自定义DLL解析,请使用app.config来定义您的自定义DLL位置,而不是程序集解析事件。配置方法更可预测,并且,嘿,它在易读的Xml中声明!融合日志查看器也很好地报告你的DLL被插入到探测链中的哪个位置,而汇编 - 解析事件将会隐藏所有你的逻辑和规则。 唯一真正的缺点是使用app.config意味着更改在你的应用程序重新读取配置文件(通常是app重新启动)之前不会生效,但是如果你正在对你的插件进行AppDomain隔离,你甚至可以解决这个问题。

与你的'core.dll'问题有关... 我认为,对你而言,这是一个相对简单的问题。 Core包DLL中的所有东西都应该存在于一个版本名称空间下(参见上面的Company.Product.v1_0等),所以它实际上是有意义的,即您的DLL也包含一个版本号。这将删除部署到bin文件夹时DLL相互覆盖的问题。 不依赖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的依赖关系,并提醒用户该插件不兼容。

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

+0

漂亮的绘画。一些平板电脑? – Wernight

+0

哈哈哈 - 不,Wacom平板电脑 - 我们对文件有一个快速的周转需求,所以它不需要浪费时间在PowerPoint等 - 只要快速敲出来,如果它坚持下来,它会得到修改...更高效。 – Adam

相关问题