I wrote this many years ago and it has just been laying around since then without being published. In other words, everything below this point is old and unedited since i wrote it back then.
This article is a “Dynamic load and unload of plugins – in 20 minutes”.
The idea is that it is that you should be able to go through this article in 20 minutes (or less) and find out if it is applicable to what you want to achieve. If you do not know how MEF (Managed Extensibility Framework) works please get up to speed and then come back to this article.
NOTE: This is not production code, it is there only as an example to show how it can be done. It is “as is” and I leave no warranties.
Short background.
A little while back, I found myself needing to implement plugins to achieve the wanted functionality of a project. But as the application was a server service and should not be stopped, I needed my plugins to be able to load and unload while the application was running.
While searching online I found some different ways to do this.
I had looked at MEF (…) earlier for the “plugin functionality”, but wanted to see what was out there.
Deciding to use MEF I now wanted to see if anyone hade done dynamic loading and unloading before (why invent the wheel…). I found some examples but most of them talked about it not being straight forward, since the loaded assemblies files are locked. And to get around that you could among other ways use “shadow copy”. If you want to look at those ways to do it, just search online and you will easily find examples or forum answers on how to do it.
But I wanted an “uncomplicated” way and my logic told me to go another way.
Lets get to the point!
My thought was simple: I should be able to read the assemblies into memory and then load them into the application. This would make it so that I could add or remove the plugin assembly files without shutting down my application. It also meant I did not need to be reliant on other functionality or 3:rd party libraries for it to work.
To make it simple I have rewritten the code to make it as simple as possible.
Start by writing the interface for the plugin. Put it in an assembly that is going to be referenced by both the application and the plugin.
//Interface that both application and plugin assembly needs to know.
[InheritedExport(typeof(IAutomationPlugin))]
public interface IAutomationPlugin
{
string Name { get; }
void DoPluginStuff();
}
Reference and implement the interface in an assembly. Do not reference the application.
//The plugin functionality that resides in the plugin assembly that is loaded by MEF
public class Executer : IAutomationPlugin
{
private const string _name = "SomePluginName";
public string Name
{
get { return _name; }
}
public void DoPluginStuff()
{
//Do plugin stuff
}
}
Now create the application and reference the interface.
After that you can add the functionality for loading the plugin assemblies into memory.
When doing this it is a good idea to check so that you do not load all .dll files in the folder into memory.
But i leave that up to you.
public static AggregateCatalog LoadPluginsToMemory(string pluginPath)
{
if (!Directory.Exists(pluginPath))
{
return null;
}
var files = Directory.GetFiles(engine.PluginPath, "*.dll");
var aggregateCatalog = new AggregateCatalog();
foreach (var file in files)
{
try
{
var ms = new MemoryStream();
var fs = new FileStream(file, FileMode.Open, FileAccess.Read);
var bytes = new byte[fs.Length];
fs.Read(bytes, 0, (int) fs.Length);
ms.Write(bytes, 0, (int) fs.Length);
fs.Close();
ms.Close();
var assemblyCatalog = new AssemblyCatalog(Assembly.Load (ms.ToArray ()));
aggregateCatalog.Catalogs.Add(assemblyCatalog);
}
catch(Exception)
{
throw;
}
}
return aggregateCatalog;
}
Once you have this, reloading plugins is simple.
I went with a cycle plugins that dumps all plugins and loads the plugins that are in the plugin folder.
This way to remove a plugin i only needed to delete the assembly file and cycle plugins.
public void CyclePlugins()
{
//Plugins is a List<IAutomationPlugin> so to remove you just clear the list
Plugins.Clear();
ImportPlugins();
}
End notes.
As I said from the beginning, this may not be the best way and there are some things you need to be cautious of. One of those things has to do with memory concerns. But all the concerns there may be I leave up to you. As it is better if you check all the aspects your self, so that you know all the aspects and ramifications of doing this implementation.