C# 将程序集作为模块/插件加载,同时避免重复和脆弱性
我们有一个相当大的C#代码库,用于一个被分成许多程序集的产品,以避免一个单一的产品,并强制执行一些代码质量标准(特定于客户的功能包含在特定于客户的程序集中,以保持“核心”通用性,并且不受特定于客户的业务逻辑的依赖性的影响)。我们在内部称这些插件,但它们更多的是组成整个产品的模块 其工作方式是将这些模块的DLL复制到一个目录中,然后应用程序运行时(ServiceStack IIS web应用程序或基于Quartz的控制台应用程序)对当前已加载程序集列表中未加载的每个模块执行C# 将程序集作为模块/插件加载,同时避免重复和脆弱性,c#,plugins,dll,.net-assembly,appdomain,C#,Plugins,Dll,.net Assembly,Appdomain,我们有一个相当大的C#代码库,用于一个被分成许多程序集的产品,以避免一个单一的产品,并强制执行一些代码质量标准(特定于客户的功能包含在特定于客户的程序集中,以保持“核心”通用性,并且不受特定于客户的业务逻辑的依赖性的影响)。我们在内部称这些插件,但它们更多的是组成整个产品的模块 其工作方式是将这些模块的DLL复制到一个目录中,然后应用程序运行时(ServiceStack IIS web应用程序或基于Quartz的控制台应用程序)对当前已加载程序集列表中未加载的每个模块执行Assembly.Loa
Assembly.LoadFile
(AppDomain.CurrentDomain.GetAssemblys()
)
这个PluginLoader
只加载plugins.config
文件中存在的程序集,但我认为这与当前的问题无关
PluginLoader
类的完整代码:
这个…可以工作。有点。但是它很脆弱,并且存在这样一个问题:程序集以这种方式加载两次,从不同的位置(通常是从应用程序的/bin/文件夹和插件目录)。这似乎是因为在调用PluginLoader
类时,AppDomain.CurrentDomain.GetAssemblys()
(启动时)不一定返回程序将自行加载的程序集的最终列表。因此,如果在/bin/中有名为dapper.dll的程序集(核心和许多插件/模块的共同依赖项)程序尚未使用,那么它将尚未加载(换句话说,它将延迟加载)。然后,如果该dapper.dll也是由插件加载的,PluginLoader
将看到它尚未加载,并将加载它。然后,当程序使用其dapper依赖项时,它将从/bin/加载dapper.dll,我们现在加载了两个dapper.dll
在大多数情况下,这似乎很好。但是,我们使用RazorEngine库,它在您尝试编译模板时抱怨使用相同名称的重复程序集
在调查过程中,我遇到了一个问题:
我尝试了公认的答案和Jon Skeet的解决方案。公认的答案有效(尽管我还没有验证是否有任何奇怪的行为)但这感觉很糟糕。首先,这也让程序尝试加载碰巧在/bin/中的本机DLL,但显然失败了,因为它们不是.NET程序集。因此,您现在必须尝试解决这个问题。我还担心如果/bin/包含一些不再实际使用的旧DLL,但现在仍然被加载,会产生奇怪的副作用。这在生产中不是问题,但在开发中是问题(事实上,这整件事在开发中更像是问题,而不是生产,但解决这一问题的附加健壮性在生产中也会得到赞赏)
如上所述,我还尝试了Jon Skeet的答案,我的实现在方法LoadReferencedAssemblys
中的PluginLoader
类的要点中可见。这有两个问题:
System.Runtime.Serialization
如果需要的话,我愿意彻底检查插件系统的工作方式。有几种方法可以解决这个问题(模块/插件部署在目录层次结构中),坦率地说,您选择了最困难的方法 最简单的方法是将所有文件夹添加到您的专用探测路径中。然后将对
Assembly.LoadFile
的所有调用替换为。这将使.NET程序集解析机制自动为您解析所有程序集。您不需要加载引用的程序集,因为它们将在nee时加载自动加载。只需使用Assembly.Load
加载模块/插件
这种方法的缺点是当以下任一情况成立时:
- 部署的程序集不驻留在应用程序库下的目录中。如果您只设置属性,则可以克服这一问题(有成本,请参阅文档中的备注)。这将使Load像LoadFrom一样工作。MEF在
中使用此方法AssemblyCatalog
- 您有不同的程序集(不仅仅是文件)使用相同的标识。如果是这种情况,则可能需要使用不同的程序集命名方法。例如,DevExpress方法具有文件名中具有程序集版本的强命名程序集。这将允许您使用平面目录结构。如果无法使用此方法,则ong命名你的程序集并将它们部署在GAC或不同的文件夹中。如果你不能做到这一点,那么你的所有程序集都将使用当前方法加载尽可能少的程序集,但请尝试一个计划,慢慢用新的强命名版本替换它们
Assembly.Codebase
属性来创建
最后看一下Suzanne Cook在上的博客。我可以问你为什么需要