Our application creates separate 'sandbox' AppDomains in order to load plug-in code, so that we can unload these plug-ins once we have finished with them.
We make use of the AssemblyResolve event in the sandbox AppDomain, in order to dynamically fix up any assemblies which are not on the sandbox's probing path(s).
When running this code in a unit test, it behaves as expected:
- New AppDomain created.
- CreateInstanceAndUnwrap called to get a proxy into the AppDomain.
- CLR loads assembly containing the proxy type, and marshalls an instance across the AppDomain boundary.
- Call our init method on proxy to hook up AssemblyResolve event inside AppDomain.
- Start doing work in app domain.
- AssemblyResolve event fired as methods are JIT'd, and handled correctly by our custom assembly resolver.
However, when we deploy it to an environment, this behaviour changes:
- New AppDomain created.
- CreateInstanceAndUnwrap called to get a proxy into the AppDomain.
- CLR tries to load ALL assemblies referenced by the assembly in which the proxy type is defined.
- Some assembly resolves fail silently (because AssemblyResolve has not been hooked up yet).
- Call init method on proxy to hook up AssemblyResolve event inside AppDomain.
- Start doing work in app domain.
- FileNotFoundException thrown ("could not load assembly..."), because the failed assembly resolution from step 4 was cached, and the CLR doesn't try and re-load the assm.
The key difference is behaviour is that the CLR tried to load every referenced assembly up-front.
I would not have expected this to happen, because none of the code in the proxy type is dependent on any of the code in the references. Why is the CLR trying to load the referenced assemblies when it hasn't JIT'd anything which needs it?