How to modify Structuremap to load assemblies without locking them
In our series of building a Windows application with a plugin support, we’ve thus far examined how Structuremap allows the developer to update and add new features into the application without turning it off. In this post we’re going to cover in detail how the mystical “AssembliesFromPathDynamically”-method works.
Out-of-the-box, Structuremap allows the developer to load external assemblies with the help of a AssemblyScanner-class. AssemblyScanner-implements the IAssemblyScanner-interface and in doing so, it has the following interesting methods:
- AssembliesFromApplicationBaseDirectory
- AssembliesFromPath
When you build your Structuremap container, you usually do that with the help of Registry-classes. Our sample application only has one registry-class, PluginRegistry:
<span style="color: #606060" id="lnum1"> 1:</span> <span style="color: #0000ff">public</span> PluginRegistry()
<span style="color: #606060" id="lnum2"> 2:</span> {
<span style="color: #606060" id="lnum3"> 3:</span> Scan( x=>
<span style="color: #606060" id="lnum4"> 4:</span> {
<span style="color: #606060" id="lnum5"> 5:</span> x.AssembliesFromPathDynamically(<span style="color: #006080">@".plugins"</span>);
<span style="color: #606060" id="lnum6"> 6:</span> x.AddAllTypesOf<IPlugin>();
<span style="color: #606060" id="lnum7"> 7:</span> });
<span style="color: #606060" id="lnum8"> 8:</span> }
The implementation is really straight forward. Structuremap scans all the assemblies in plugins-directory and adds all the classes which implement the IPlugin-interface into the container. In that code sample you can see our mystery-method again. Here’s why we need it (taken from the Structuremap’s AssemblyScanner-class):
<span style="color: #606060" id="lnum1"> 1:</span> assembly = System.Reflection.Assembly.LoadFrom(assemblyPath);
The default behavior for the Structuremap is to load the assembly using System.Reflection.Assembly-class. That causes problems because it makes the Structuremap to lock the dll. And because it’s locked, we can’t update it if our application is running. Here’s what will happen in our sample application if you replace the call to AssembliesFromPathDynamically with AssembliesFromPath in our sample application and click the “Update my features”-button:
And this is why we’re using the AssembliesFromPathDynamically instead of the built-in AssembliesFromPath-method in our sample application. It allows the Structuremap to load the assembly into our application without locking it. Instead of calling the Assembly.LoadFrom with a file path, we pass it a byte array:
<span style="color: #606060" id="lnum1"> 1:</span> <span style="color: #0000ff">byte</span>[] assemStream = <span style="color: #0000ff">null</span>;
<span style="color: #606060" id="lnum2"> 2:</span> <span style="color: #0000ff">using</span> (FileStream fs = <span style="color: #0000ff">new</span> FileStream(assemblyPath, FileMode.Open))
<span style="color: #606060" id="lnum3"> 3:</span> {
<span style="color: #606060" id="lnum4"> 4:</span> assemStream = <span style="color: #0000ff">new</span> <span style="color: #0000ff">byte</span>[fs.Length];
<span style="color: #606060" id="lnum5"> 5:</span> fs.Read(assemStream, 0, (<span style="color: #0000ff">int</span>)fs.Length);
<span style="color: #606060" id="lnum6"> 6:</span> }
<span style="color: #606060" id="lnum7"> 7:</span>
<span style="color: #606060" id="lnum8"> 8:</span> assembly = System.Reflection.Assembly.Load(assemStream);
You can use the Structuremap.dll from our sample application if you want to try the dynamic loading of assemblies in your own application. Or, you can download and build the required source code from this Structuremap fork.
Please note that for some reason I had hard time to work with the fork. Git marked all the files as changes just after cloning the repository. Changing the Git’s line ending settings didn’t help. So I decided to manually enter the changes in GitHub. I’m sorry for all the typos.
Update 21.8.2010:
I decided to remove the AssembliesFromPathDynamically-method and instead added a new overload to the AssembliesFromPath-method. This method now has a third parameter called “loadWithoutLocking (bool)” and setting it to true I think this change keeps the IAssemblyScanner-interface cleaner.
The sample application has been updated to reflect this change.