我需要动态解析从一个类库到另一个类库的程序集引用。类库是从 PowerShell 脚本加载的,因此在可执行文件中查找依赖程序集的默认 .NET 行为会直接失败,因为可执行文件就是 PowerShell 本身。如何使这些依赖程序集引用正确解析/工作?
更详细地说:
我有两个实用程序库:一个是核心库,另一个是执行一些非常具体的解析任务的库。我想在 PowerShell 脚本中动态加载它们,而不将它们安装在 GAC 中。第二个库依赖于第一个库。在VS解决方案中,解析库有一个对核心库的项目引用,带有Copy Local= true。
使用后,我可以从解析库输出 bin (/Debug|/Release) 文件夹加载和使用这两个库(此处为 PowerShell):
[Reflection.Assembly]::LoadFile("C:\...thefile.dll")
但是,每当调用解析(依赖)库中的方法来调用核心库中的某些内容时,它都无法解析核心程序集。这……令人沮丧……因为这些文件位于同一文件夹中。其中一个或两个都具有强名称键并没有什么区别。
我现在的解决方法是处理该AssemblyResolve事件。棘手的事情是弄清楚将其放在类库中的何处,因为没有像可执行方法中那样始终在其他任何内容之前执行的单一入口点Main()(请参阅Is there an equivalent of a application_Start for a class library in c#) 。
现在,我已经创建了一个Resolver带有静态构造函数的静态类AssemblyResolve,该静态构造函数附加了 的处理程序,然后在每个解析类中都有一个引用静态解析器类的静态构造函数,强制执行解析器类的静态构造函数。结果是,AssemblyResolve 事件仅附加一次并使用通用的中央代码进行处理。所以它有效。但我讨厌必须向我的所有解析类添加一个时髦的静态构造函数。
有更好的方法来处理这个问题吗?
我找到了一个遵循“消费者应该解决”模式的解决方案,并且适用于 PowerShell 和普通 .NET 应用程序消费者。
想法:
Resolver从同一个或另一个类库调用该类,而是由消费者直接调用它。当 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-ObjectEventcommandlet 注册它。这可行,但有一个问题:PowerShell 不支持返回值的事件处理程序。AssemblyResolve 处理程序返回一个 Assembly 对象。这几乎就是他们的全部目的。
| 归档时间: | 
 | 
| 查看次数: | 5701 次 | 
| 最近记录: |