How to use the features of Structuremap to implement plugin-support
In our first view on “How to update your .NET-application on the fly” we saw a simple Windows Forms application which allowed the user to update its features on the fly. What wasn’t shown was how the application used Structuremap’s features to implement the plugin-support. This post will cover that detail.
When a user clicks the “Run my features” –button, the following code is executed:
<span id="lnum1" style="color: #606060"> 1:</span> var plugins = container.GetAllInstances<IPlugin>();
<span id="lnum2" style="color: #606060"> 2:</span> <span style="color: #0000ff">if</span> (plugins == <span style="color: #0000ff">null</span>)
<span id="lnum3" style="color: #606060"> 3:</span> <span style="color: #0000ff">return</span>;
<span id="lnum4" style="color: #606060"> 4:</span>
<span id="lnum5" style="color: #606060"> 5:</span> <span style="color: #0000ff">foreach</span> (var plugin <span style="color: #0000ff">in</span> plugins)
<span id="lnum6" style="color: #606060"> 6:</span> {
<span id="lnum7" style="color: #606060"> 7:</span> plugin.Run();
<span id="lnum8" style="color: #606060"> 8:</span> }
As you can see, the application asks from the container for all the class instances which implement the IPlugin-interface. After that, the collection is just enumerated and each plugin is executed. There’s no magic in there. What happens when user clicks the “Update my features”-button is much more interesting:
<span id="lnum1" style="color: #606060"> 1:</span> var appPath = Application.StartupPath;
<span id="lnum2" style="color: #606060"> 2:</span>
<span id="lnum3" style="color: #606060"> 3:</span> var sourcePath = Path.Combine(appPath, <span style="color: #006080">@"pluginarchiveplugin.dll"</span>);
<span id="lnum4" style="color: #606060"> 4:</span> var destinationPath = Path.Combine(appPath, <span style="color: #006080">@"pluginsplugin.dll"</span>);
<span id="lnum5" style="color: #606060"> 5:</span>
<span id="lnum6" style="color: #606060"> 6:</span> File.Copy(sourcePath, destinationPath, <span style="color: #0000ff">true</span>);
<span id="lnum7" style="color: #606060"> 7:</span>
<span id="lnum8" style="color: #606060"> 8:</span> ReloadPlugins();
There’s couple things going on in here. First, take a note on the File.Copy. Remember how we already had the “plugin.dll” in our plugins-folder? Shouldn’t this copy operation fail because that dll has already been loaded into the app domain, thus locking the file? Well, it doesn’t fail, meaning the Structuremap hasn’t locked the file. Also, in the last line, there’s a call to ReloadPlugins-method. That method uses some nice functionalities provided by the Structuremap:
<span id="lnum1" style="color: #606060"> 1:</span> container.EjectAllInstancesOf<IPlugin>();
<span id="lnum2" style="color: #606060"> 2:</span>
<span id="lnum3" style="color: #606060"> 3:</span> container.Configure(x =>
<span id="lnum4" style="color: #606060"> 4:</span> x.AddRegistry<PluginRegistry>()
<span id="lnum5" style="color: #606060"> 5:</span> );
In the first line, the container is asked to eject all the instances of classes which implement the IPlugin-interface. If we would call GetAllInstances<IPlugin> after that, the container wouldn’t return anything. In the third line, we’re using the Configure-method to adjust the container on run time. Here we ask it to configure the container with the PluginRegistry. PluginRegistry-class itself looks like this:
<span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> PluginRegistry : Registry
<span id="lnum2" style="color: #606060"> 2:</span> {
<span id="lnum3" style="color: #606060"> 3:</span> <span style="color: #0000ff">public</span> PluginRegistry()
<span id="lnum4" style="color: #606060"> 4:</span> {
<span id="lnum5" style="color: #606060"> 5:</span> Scan( x=>
<span id="lnum6" style="color: #606060"> 6:</span> {
<span id="lnum7" style="color: #606060"> 7:</span> x.AssembliesFromPathDynamically(<span style="color: #006080">@".plugins"</span>);
<span id="lnum8" style="color: #606060"> 8:</span> x.AddAllTypesOf<IPlugin>();
<span id="lnum9" style="color: #606060"> 9:</span> });
<span id="lnum10" style="color: #606060"> 10:</span> }
<span id="lnum11" style="color: #606060"> 11:</span> }
The PluginRegistry adds all the assemblies from the plugins-folders and asks the Structuremap container to find all the classes which implement the IPlugin-interface.
If you know the Structuremap’s api well, you may be wondering where that AssembliesFromPathDynamically came from. We will get to that in the next post.
Download
Like mentioned in the first post of this series, the full source code for the sample application is available from the GitHub.