Load Assemblies Without Locking
I have been working through a way to load assemblies without locking them so that they can be replaced while the application is running. There is the more popular way of handling this by creating a new AppDomain and using shadowing, but I didn't want to have all the messaging marshalled and/or serialized for the entire application. Plus, I didn't really need the ability to unload the assemblies. The solution...move the assemblies into a directory that is not in the private bin path and use the Assembly.Load method that takes a byte[] of the assembly file. You can also pass in a byte[] of the symbol data (the .pdb file) so that you can still have debugging. Neat!
The problem that then comes up is loading any assemblies that are referenced by dynamically loaded assembly. The first option is to put the dependency in either the application root folder or add a private bin path using AppDomain.CurrentDomain.AppendPrivatePath. (this is deprecated in .NET 2.0, but you can add a section to the app.config that will achieve the same results...see here) This configures the probing logic for the application. But, the problem here is that assemblies loaded via this mechanism will be locked. The solution...the AppDomain.AssemblyResolve event. This lets you catch the situation where an assembly can't be found, and then offer a way to supply the correct assembly. The handler for the event passes the full name of the assembly that is requested and requires a return of an assembly instance. Double neat!
My complete flow ended up as this:
The problem that then comes up is loading any assemblies that are referenced by dynamically loaded assembly. The first option is to put the dependency in either the application root folder or add a private bin path using AppDomain.CurrentDomain.AppendPrivatePath. (this is deprecated in .NET 2.0, but you can add a section to the app.config that will achieve the same results...see here) This configures the probing logic for the application. But, the problem here is that assemblies loaded via this mechanism will be locked. The solution...the AppDomain.AssemblyResolve event. This lets you catch the situation where an assembly can't be found, and then offer a way to supply the correct assembly. The handler for the event passes the full name of the assembly that is requested and requires a return of an assembly instance. Double neat!
My complete flow ended up as this:
- Load all assemblies from a specific directory that is not probe-able into a new AppDomain
- Create a dictionary containing infromation about all the assemblies, their dependencies, and paths, and return this to the original AppDomain.
- Unload the domain
- Add the AppDomain.AssemblyResolve listener, and when it is called, use the name given to the method to look up which assembly needs loading from the dictionary.
- Load it using the Assembly.Load that takes raw byte arrays for the assembly and symbol data
