在哪里办理AssemblyResolve事件在类库? [英] Where to handle AssemblyResolve event in a class library?

查看:123
本文介绍了在哪里办理AssemblyResolve事件在类库?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



更详细的:



我有两个公用程序库:核心之一,另外一个是做一些非常具体的解析任务。我想动态加载它们PowerShell脚本的没有在GAC中安装这些。第二个库取决于第一个。在VS的解决方案,解析库有一个项目,参考核心库,用复制本地 = 真正



我可以加载并使用这两个库从解析库出纸槽(/调试| /释放)(这里的PowerShell)使用后的文件夹:



<:

  [Reflection.Assembly] ::的LoadFile(\ ... thefile.dll C) / pre> 

然而,只要在解析(依赖)库调用从核心库的东西调用一个方法它未能解决的核心组件。这是令人沮丧... ...自从该文件是在同一文件夹。这没有什么区别一方或双方是否具有强名称密钥。



我现在的解决方法是处理 AssemblyResolve 事件。棘手的是搞清楚在哪里把这个类库,因为没有单一入口点像有一个可执行,将永远别的之前执行的Main()方法(参见是还有的Application_Start的在C#一个类库等效)。



现在我做了一个静态类与附加一个处理程序<$ C静态构造函数$ C> AssemblyResolve ,然后有一个静态构造函数在每个指静态解析类解析班,迫使解析类的静态构造函数来执行。其结果是,该AssemblyResolve事件被附恰好一次,并与共同的中央码处理。因此,它的工作原理。但我讨厌不得不一个时髦的静态构造函数添加到我所有的解析类。



有没有更好的方式来处理这个问题?


解决方案

我想通了,后面的消费者应该可以解决模式的解决方案,并同时适用于PowerShell和.NET正常。应用程序的消费者



理念:




  • 请一类具有一个内部AssemblyResolve事件处理程序,并,重视处理程序的AppDomain.CurrentDomain.AssemblyResolve事件静态构造函数。 (到目前为止,这是熟悉的。)

  • 而不是从同一个或另一个类库的调用调用类的它直接由消费者的。当PowerShell是消费者,从PowerShell中调用

  • 这工作,因为任何一个消费者 - 包括PowerShell的 - 有相同的 CurrentDomain 在加载组件。因此,即使该事件处理程序中的一些动态加载的程序集连接,它仍然会在一个组装决心在主要消费应用程序无法调用。



我的的版本有:




  • 的静态属性 AssemblyDirectory 可用于任意地设定从搜索目录。如果留空,将使用从 Assembly.GetCallingAssembly()中的目录位置

  • 一个虚拟的寄存器()方法,它确实没有什么,除了确保静态构造函数被调用



PowerShell代码:

 #首先,加载程序集的解析器类。其实,我把它放在同一
#核心库,但它其实并不重要在哪里。它甚至可以是单独使用Add-类型命令行
[Reflection.Assembly] ::的LoadFile([一个具有解析器类组件] .DLL)内置美元的动态编译的程序集B $ B#

#调用注册方法强制静态构造函数来运行,如果它尚未
[mycorp.mylib.Resolver] ::寄存器()

$ sourcedir =C:\foo\bar\..some随机DIR
#设置路径搜索未解决的组件
[mycorp.mylib.Resolver] :: AssemblyDirectory = $ sourcedir

#加载依赖程序集
[Reflection.Assembly] ::的LoadFile($ sourcedir\myparser.dll)

#创建并使用一个对象从依赖程序集。当核心组件
#起初未能解决,解析器的处理程序自动调用,
#,一切都桃色
$ =编码器的新对象mycorp.mylib.anamespace.aclass
$结果= $ encoder.DoStuff($ X,$ Y,$ Z ...等),

如果你想知道如何实际处理AssemblyResolve事件,请在MSDN上的文档: AppDomain.AssemblyResolve事件



关于注册-ObjectEvent



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


I need to dynamically resolve assembly references from one class library to another. The class libraries are being loaded from a PowerShell script, so the default .NET behaviour of looking for dependent assemblies in the executable directly fails, as the executable is PowerShell itself. How do I make these dependent assembly references resolve / work correctly?

In more detail:

I have two utility libraries: a core one and another one that does some very specific parsing tasks. I want to load them dynamically in a PowerShell script without installing them in the GAC. The second library depends on the first. In the VS solution, the parsing library has a project reference to the core library, with Copy Local = true.

I can load and use both libraries from the parsing library output bin (/Debug|/Release) folder after using (PowerShell here):

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

However, whenever calling a method in the parsing (dependent) library that calls something from the core library it fails to resolve the core assembly. This is...frustrating...since the files are in the same folder. It makes no difference whether one or both have strong name keys.

My workaround now is to handle the AssemblyResolve event. The tricky thing is figuring out where to put this in a class library, since there's no single entry point that will always execute before anything else like there is in an executable Main() method (see Is there an equivalent of Application_Start for a class library in c#).

For now I've made a static Resolver class with a static constructor that attaches a handler for AssemblyResolve, and then have a static constructor in each of the parsing classes which refers to the static resolver class, forcing the resolver class's static constructor to execute. The result is that the AssemblyResolve event gets attached exactly once and handled with common, central code. So it works. But I hate having to add a funky static constructor to all of my parsing classes.

Is there a better way to handle this?

解决方案

I figured out a solution that follows the "consumer should resolve" pattern, and which works for both PowerShell and normal .NET application consumers.

The idea:

  • Make a class with an internal AssemblyResolve event handler, and a static constructor that attaches the handler to the AppDomain.CurrentDomain.AssemblyResolve event. (So far this is familiar.)
  • Instead of invoking the Resolver class from the same or another class library, invoke it directly by the consumer. When PowerShell is the consumer, invoke the Resolver from PowerShell.
  • This works because any consumer--including PowerShell--has the same CurrentDomain as the assemblies it loads. So even if the event handler is attached in some dynamically loaded assembly, it will still be invoked when a assembly resolve fails in the main consuming application.

My version of Resolver has:

  • A static property AssemblyDirectory that can be used to optionally set the directory to search from. If left blank, it will use the directory found from Assembly.GetCallingAssembly().Location
  • A dummy Register() method which really does nothing except ensure the static constructor has been called.

PowerShell Code:

# 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 )

If you want to know how to actually handle the AssemblyResolve event, check out the documentation on MSDN: AppDomain.AssemblyResolve Event

Regarding Register-ObjectEvent:

At first I tried to build an assembly resolver directly in PowerShell, and register it using the Register-ObjectEvent commandlet. This would work, but for one problem: PowerShell doesn't support event handlers that return a value. AssemblyResolve handlers return an Assembly object. That's almost their whole purpose.

这篇关于在哪里办理AssemblyResolve事件在类库?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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