I am trying to use MEF (The version that is built in mono) for a simple pluginsystem to extend my application. I followed various tutorials that are running well with Microsofts .NET framework under windows, but (same code) failing under linux(kernel 3.7.3 - gentoo) with mono-2.10.9.
I created an interface, that all Exports (my extensions) have to reference to in an external assembly:
namespace PlugInInterface {
[InheritedExport]
public interface IPlugIn {
void Execute();
}
}
Then I created a Hostapplication (a ConsoleApplication) that also references to the PluginInterface:
namespace Host {
class MainClass {
private static PluginHost plugins;
public static void Main(string[] args) {
plugins = new PluginHost();
foreach(IPlugIn plugin in plugins.plugins) {
plugin.Execute();
}
}
}
class PluginHost{
[ImportMany(typeof(IPlugIn))]
public List<IPlugIn> plugins = new List<IPlugIn>();
public PluginHost() {
var catalog = new DirectoryCatalog("PlugIns");
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
}
And one Extension that gets compiled in the "PlugIns" subfolder of the Debug directory, where the host-application is placed:
namespace TestPlugin1 {
[Export(typeof(IPlugIn))]
public class TestPlugin1 : IPlugIn {
public void Execute() {
Console.WriteLine("Hello, my Name is TestPlugin_Implementation1");
}
}
}
The DirectoryCatalog loads the PlugIn's file ("./PlugIns/TestPlugin1.dll") but it does not recognize it as an Export (a "Part") so the plugins
list remains empty.
It works, if I put the PluginFile in the application's workingdirectory and change the DirectoryList to:
var catalog = new DirectoryCatalog("");
If I run the compiled version that has been compiled from the .NET compiler with mono, the PlugIn is loaded twice.
I tried to replace the DirectoryCatalog manually by loading the PlugIn-files with System.Reflection.Assembly.LoadFile(...);
and then using AssemblyCatalogs to load all found assemblies:
class PluginHost{
[ImportMany(typeof(IPlugIn))]
public List<IPlugIn> plugins = new List<IPlugIn>();
public PluginHost() {
List<AssemblyCatalog> catalogs = new List<AssemblyCatalog>();
foreach(string plfile in Directory.GetFiles("PlugIns", "*.dll", SearchOption.AllDirectories)) {
catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.LoadFile(plfile)));
}
var container = new CompositionContainer(new AggregateCatalog(catalogs));
container.ComposeParts(this);
}
}
This works, but the same as with the compiled .NET executable: the PlugIn loads twice.
Is this a bug of mono and it's implementation of MEF, or am I doing something wrong?
If I run the compiled version that has been compiled from the .NET compiler with mono, the PlugIn is loaded twice.
This happens because although you have IPlugIn
decorated with InheritedExportAttribute
you also decorate the plugin with the ExportAttribute
. The catalog will match both of these and hence export twice. You need just one of the two approaches. Either keep the InheritedExportAttribute
and do not decorate any implementation with the ExportAttribute
or decorate all implementations with ExportAttribute
and remove the InheritedExportAttribute
from the interface.
I haven't figured out why DirectoryCatalog
cannot load the plugin assembly on Mono. On CLR is works correctly as you mentioned.