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:
Resolver
class from the same or another class library, invoke it directly by the consumer. When PowerShell is the consumer, invoke theResolver
from PowerShell.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:AssemblyDirectory
that can be used to optionally set the directory to search from. If left blank, it will use the directory found fromAssembly.GetCallingAssembly().Location
Register()
method which really does nothing except ensure the static constructor has been called.PowerShell Code:
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.You can register the event with the AppDomain (see here), but that has to be registered/handled in many, many places because for each instance you cannot guarantee that the registration has already taken place at any given entry point.
It's probably better practice to handle this in the application that is using the class library rather than in the library itself.