I have two assemblies: App
and AddOn
. App
references AddOn
, but CopyLocal
is set to false
, since AddOn
will be loaded dynamically by App
.
Here is the code in AddOn
:
namespace AddOn
{
public class AddOnClass
{
public static void DoAddOnStuff()
{
Console.WriteLine("AddOn is doing stuff.");
}
}
}
and here is the code in App
:
class Program
{
static void Main(string[] args)
{
Assembly.LoadFrom(@"..\..\..\AddOn\bin\Debug\AddOn.dll");
// Without this event handler, we get a FileNotFoundException.
// AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
// {
// return AppDomain.CurrentDomain.GetAssemblies()
// .FirstOrDefault(a => a.FullName == e.Name);
//};
CallAddOn();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void CallAddOn()
{
AddOnClass.DoAddOnStuff();
}
}
What I don't understand is why the code doesn't work with the AssemblyResolve
handler commented in Main()
. When run in Visual Studio, the debugger breaks on CallAddOn()
with a FileNotFoundException
. Why is it complaining? The assembly is loaded, and it's the exact same version (i.e. same file on disk) as what was referenced by App
.
I feel like there is some fundamental concept that I'm not understanding properly here. The commented AssemblyResolve
handler works fine, but it seems like a hack and I don't understand why I need it because it seems like it's doing something trivial.
AssemblyResolve event for applications that require greater control over assembly loading. By handling this event, your application can load an assembly into the load context from outside the normal probing paths, select which of several assembly versions to load, emit a dynamic assembly and return it, and so on.
AssemblyResolve event, to handle the attempt to load an assembly when the automatic CLR search has failed.
The reason is that there are multiple assembly loading contexts. The context which an assembly is loaded into affects how it can be used. When an assembly is loaded by the runtime using the default probing mechanism it is put into the so-called Load context. This is the context used when you load an assembly via Assembly.Load
. You have loaded the assembly using LoadFrom
which uses its own context. Probing does not examine the LoadFrom context and the file is not in the probing path so you are required to resolve it for the runtime. This is not symmetric however. If an assembly is loaded in the Load context, LoadFrom
will load it from there first (assuming the identity is the same. for unsigned assemblies, the path is part of the identity.). I will note there are more contexts including ReflectionOnlyLoad
and ReflectionOnlyLoadFrom
. LoadFile
loads an assembly without a context, i.e. all dependencies must be manually loaded.
If you want assemblies to be resolved in the Load context, but have them exist outside the application's default probing path you can do it via configuration as well. Use either the <codebase>
element of an assembly binding redirect or the privatePath
attribute of the <probing>
element.
Read this for more information. There are also some blog posts by Suzanne Cook from a while back on assembly loading and contexts (see here, here, and here).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With