I'm trying to create a simple plugin system using MEF2.
In my scenario I'll have only 1-10 plugins, so I decided not to bother about loading assemblies in a separate domain to avoid performance issues; this would be nice anyway, but it seems it poses some constraints on the exported objects (must be [Serializable]
and derive from MarshalByRefObject
: see https://msdn.microsoft.com/en-us/library/ms972968.aspx and http://www.codeproject.com/Articles/1091726/The-Nuances-of-Loading-and-Unloading-Assemblies-wi and https://msdn.microsoft.com/en-us/magazine/jj133818.aspx), and I need them to be POCO objects as I'll have no control on their source.
I just have to load all the assemblies exporting types derived from a specific interface, so I created a simple catalog class, which scans a directory to collect assemblies, and can return a MEF2 container configured with conventions exporting all the types derived from T (see below).
Anyway, when I try to use it, I cannot manage to have any export in the catalog, even if the container assembly is correctly found and loaded. Surely I'm missing something in the MEF2 export conventions, could anyone point me to a solution?
You can find a full dummy repro solution here https://onedrive.live.com/redir?resid=F8DEF93587EC2C1!258076&authkey=!AKSKUhMwFhV0n-k&ithint=file%2czip .
Here is the relevant code and a usage sample:
public sealed class PluginCatalog<T>
{
private readonly string _sFileMask;
private readonly List<Assembly> _aAssemblies;
private bool _bLoaded;
public string SourceDirectory { get; }
public bool IsRecursive { get; }
public PluginCatalog(string directory, string fileMask = "*.dll", bool recursive = false)
{
_aAssemblies = new List<Assembly>();
_sFileMask = fileMask;
SourceDirectory = directory;
IsRecursive = recursive;
}
private void LoadPluginAssemblies(string directory)
{
if (directory == null) throw new ArgumentNullException(nameof(directory));
// scan DLLs
foreach (string sFilePath in Directory.GetFiles(directory, _sFileMask))
{
Assembly asm = Assembly.LoadFrom(sFilePath);
if (asm.GetTypes().Any(typeof(T).IsAssignableFrom)) _aAssemblies.Add(asm);
}
// scan subdirectories if required
if (IsRecursive)
{
foreach (string sSubdir in Directory.GetDirectories(directory))
LoadPluginAssemblies(sSubdir);
}
_bLoaded = true;
}
public CompositionHost GetContainer()
{
if (!_bLoaded) LoadPluginAssemblies(SourceDirectory);
// export all the types implementing the requested interface type
ConventionBuilder builder = new ConventionBuilder();
builder.ForTypesMatching(t => typeof(T).IsAssignableFrom(t)).Export<T>();
// create the container from all the plugins assemblies
return new ContainerConfiguration()
.WithAssemblies(_aAssemblies)
.CreateContainer();
}
}
Usage sample:
PluginCatalog<IAnimal> catalog = new PluginCatalog<IAnimal>(directoryName, "*Plugin*.dll");
CompositionHost container = catalog.GetContainer();
var animals = container.GetExports<IAnimal>().ToList();