When an assembly needs to be loaded
into an AppDomain, the .NET CLR will look in all sorts of places before giving up and throwing a FileNotFoundException in
your face. Actually I've always been impressed with how well this part of the
CLR works, being able to just drop files into directories without having to
change code, re-directing bindings and dictating how the CLR should deal with
newer versions of an assembly through configuration files, etc.
There are situations however, where
this built-in functionality is not enough. In those cases you need to provide
some custom logic for resolving a reference to an assembly and you can do this
using the AppDomain.AssemblyResolve event. When the CLR fails to resolve an assembly reference,
it will fire this event as a last resort hoping that someone will do the hard
work and locate the assembly for it. The arguments of the event provide you
with the full name of the assembly in question, which you need to use to
determine which assembly to use. This is a tremendously flexible solution, but
there are some things to watch out for
- Make sure that for a specific full name, you always
provide the CLR with a reference to the same assembly. If you don't you
will experience some very weird issues (failing to cast being one of
them) which will be difficult to trace back to this piece of code.
- If you build this logic into an assembly you are
possibly limiting its re-usability. Because this is an event and not a
method that can be overridden, there is no way of stopping it from being
executed unless you provide the CLR with the assembly through the usual
means.
- When you have multiple dependencies between assemblies,
things can get tricky. Tim Weaver
felt this pain recently.
- If you load an assembly either from disk or from memory
(i.e. Assembly.Load(byte[])) in the event handler of the
AppDomain.AssemblyResolve event, the CLR (actually Fusion) doesn't keep
track of it. This means that the second time the same assembly is needed,
although you might have already loaded the assembly into the current
AppDomain, the AppDomain.AssemblyResolve event will be fired again! You
need to implement your own caching mechanism to avoid loading the same
assembly into the same AppDomain multiple times (which of course will
result in plenty of errors that initially make little sense).
Suzanne Cook has a nice post on Assembly
identity that is relevant to the last bullet point here