在类库中处理AssemblyResolve事件的位置?

我需要动态地解决从一个类库到另一个类库的程序集引用。 类库是从PowerShell脚本加载的,因此在可执行文件中查找依赖程序集的默认.NET行为会直接失败,因为可执行文件是PowerShell本身。 如何使这些依赖的程序集引用解析/正常工作?

更详细

我有两个实用程序库:一个核心库和另一个执行一些非常具体的解析任务的实用程序库。 我想在PowerShell脚本中动态加载它们, 而无需在GAC中安装它们 。 第二个库取决于第一个库。 在VS解决方案中,解析库具有对核心库的项目引用, Copy Local = true

使用后,我可以在解析库输出bin(/ Debug | / Release)文件夹中加载和使用这两个库(PowerShell here):

 [Reflection.Assembly]::LoadFile("C:\...thefile.dll") 

但是,每当在解析(依赖)库中调用一个从核心库调用某些东西的方法时,它就无法解析核心程序集。 这是令人沮丧的…因为文件在同一个文件夹中。 一个或两个是否具有强名称密钥没有区别。

我现在的解决方法是处理AssemblyResolve事件。 棘手的是找出将它放在类库中的位置,因为在可执行的Main()方法之前没有任何单一的入口点总是会先执行其他任何东西(请参阅类库中是否存在等效的Application_Start)在c# )。

现在我已经创建了一个带有静态构造函数的静态Resolver类,它为AssemblyResolve附加了一个处理程序,然后在每个解析类中都有一个静态构造函数,它引用了静态解析程序类,强制解析器类的静态构造函数执行。 结果是AssemblyResolve事件只被连接一次,并使用通用的中央代码进行处理。 所以它有效。 但我不想在我的所有解析类中添加一个时髦的静态构造函数。

有没有更好的方法来处理这个?

我找到了一个遵循“消费者应该解决”模式的解决方案,它适用于PowerShell和普通的.NET应用程序使用者。

想法:

  • 创建一个具有内部AssemblyResolve事件处理程序的类,以及一个将处理程序附加到AppDomain.CurrentDomain.AssemblyResolve事件的静态构造函数。 (到目前为止,这是熟悉的。)
  • 而不是从相同或另一个类库调用Resolver类, 而是由使用者直接调用它 。 当PowerShell是使用者时,从PowerShell调用Resolver
  • 这是有效的,因为任何消费者 – 包括PowerShell – 都与它加载的程序集具有相同的CurrentDomain 。 因此,即使事件处理程序附加在某个动态加载的程序集中,当主要使用应用程序中的程序集解析失败时,它仍将被调用。

我的Resolver版本有:

  • 一个静态属性AssemblyDirectory ,可用于选择性地设置要搜索的目录。 如果留空,它将使用从Assembly.GetCallingAssembly().Location找到的目录
  • 一个虚拟的Register()方法,除了确保调用静态构造函数之外什么都不做。

PowerShell代码:

 # First, load the assembly with the Resolver class. I actually put it in the same # core library, but it really doesn't matter where it is. It could even be by itself # in a dynamically compiled assembly built using the Add-Type commandlet [Reflection.Assembly]::LoadFile("[an assembly that has the Resolver class].dll") # Call the Register method to force the static constructor to run if it hasn't already [mycorp.mylib.Resolver]::Register() $sourcedir = "C:\foo\bar\..some random dir" # Set the path to search for unresolved assemblies [mycorp.mylib.Resolver]::AssemblyDirectory = $sourcedir # Load the dependent assembly [Reflection.Assembly]::LoadFile("$sourcedir\myparser.dll") # Create and use an object from the dependent assembly. When the core assembly # fails at first to resolve, the Resolver's handler is automatically called, # and everything is peachy $encoder = New-Object mycorp.mylib.anamespace.aclass $result = $encoder.DoStuff( $x, $y, $z ... etc ) 

如果您想知道如何实际处理AssemblyResolve事件,请查看MSDN上的文档: AppDomain.AssemblyResolve Event

关于Register-ObjectEvent

起初我尝试直接在PowerShell中构建程序集解析程序,并使用Register-ObjectEvent命令行开关注册它。 这可行,但是对于一个问题:PowerShell不支持返回值的事件处理程序。 AssemblyResolve处理程序返回一个Assembly对象。 这几乎是他们的全部目的。

您可以使用AppDomain注册事件(请参阅此处 ),但必须在许多地方注册/处理,因为对于每个实例,您无法保证在任何给定的入口点已经进行了注册。

在使用类库而不是库本身的应用程序中处理此问题可能更好。