1

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();
Naftis
  • 4,393
  • 7
  • 63
  • 91
  • why don't you use the directory catalog to load the plugins? https://msdn.microsoft.com/en-us/library/system.componentmodel.composition.hosting.directorycatalog%28v=vs.110%29.aspx – doerig Apr 20 '16 at 07:50
  • if you need to import plugins from different subdirectories: recurse through the directories and create a DirectoryCatalog for each. Then, combine all of the DirectoryCatalogs with an AggregateCatalog to create the container. – doerig Apr 20 '16 at 07:55
  • Thanks, but here I'm using Mef2, no catalog there. – Naftis Apr 20 '16 at 08:01
  • 1
    Never mind, found it. I forgot to add my configured conventions in the WithAssemblies method call. Sorry to bother, but hope this can help some MEF2 newbie like me, and it will be useful to know if anybody comes out with better solutions for scanning assemblies without having to load all of them in the current appdomain. – Naftis Apr 20 '16 at 14:13
  • Please add the final code that worked for you as an answer - this helped me and at first I wasn't sure if you found a solution. Thanks! – Ani Feb 03 '19 at 07:45

0 Answers0