Using MEF in .NET Core

For those who don’t know, the Managed Extensibility Framework (MEF) is alive and well, and has been ported to .NET Core as Microsoft.Composition (source here). Not all of MEF has been ported, just MEF 2 (System.Composition), not MEF 1 (System.ComponentModel.Composition), meaning, we don’t have the catalogs, which included, among others, the ability to load types from assemblies in a directory.

Using MEF is straightforward, and this time I will not go into details. What I will show is how we can load types from files in a directory.

First, a sample usage might be:

var conventions = new ConventionBuilder();

conventions

    .ForTypesDerivedFrom<IPlugin>()

    .Export<IPlugin>()

    .Shared();

 

var assemblies = new[] { typeof(MyPlugin).GetTypeInfo().Assembly };

 

var configuration = new ContainerConfiguration()

    .WithAssemblies(assemblies, conventions);

 

using (var container = configuration.CreateContainer())

{

    var plugins = container.GetExports<IPlugin>();

}

Notice that we have to build the assemblies list ourselves.

Now, let’s load assemblies from a folder instead. We’ll create an extension method for that purpose:

public static class ContainerConfigurationExtensions

{

    public static ContainerConfiguration WithAssembliesInPath(this ContainerConfiguration configuration, string path, SearchOption searchOption = SearchOption.TopDirectoryOnly)

    {

        return WithAssembliesInPath(configuration, path, null, searchOption);

    }

 

    public static ContainerConfiguration WithAssembliesInPath(this ContainerConfiguration configuration, string path, AttributedModelProvider conventions, SearchOption searchOption = SearchOption.TopDirectoryOnly)

    {

        var assemblies = Directory

            .GetFiles(path, "*.dll", searchOption)

            .Select(AssemblyLoadContext.GetAssemblyName)

            .Select(AssemblyLoadContext.Default.LoadFromAssemblyName)

            .ToList();

 

        configuration = configuration.WithAssemblies(assemblies, conventions);

 

        return configuration;

    }

}

The key here is the AssemblyLoadContext class: this is what allows us to obtain an assembly from a file stream or a byte array. Notice that in .NET Core, things have changed significantly, and now we don’t have AppDomain or Assembly.LoadFrom, and assemblies are now loaded by an assembly loader, in a similar way to what Java does with class loaders.

So, now you can change the code to:

var path = @"c:\some\path";

 

var configuration = new ContainerConfiguration()

    .WithAssembliesInPath(path, conventions);

It even works for loading assemblies in all sub-folders.

Maybe I’ll write another post on MEF in the near future, so stay tuned!

Published by

Ricardo Peres

Team Leader at Dixons Carphone. Microsoft MVP.

One thought on “Using MEF in .NET Core”

  1. I have a class CatalogLoader that catalogs elements of type from files in directories, done with MEF 1 and works Ok.
    Now I want to migrate the Plugins to .Net Standard 2.0 so can be loaded from different platforms (Wpf, Uwp, Windows 10 Iot).
    This class is very interesting, but I can’t load all the library because is in .Net Core.
    What do you suggest me to find a simple solution to load the plugins?

    This is the code of the actual class.
    Thanks for you help.

    public class CatalogLoader
    {
    #region Propiedades ———————————————————————
    /// Coleccion de plugins detectados
    /// The plugins.
    [ImportMany(AllowRecomposition = true)]
    public IEnumerable<Lazy> Plugins { get; set; }
    #endregion

    #region Constructor ———————————————————————
    /// Constructor parametrizado con path de acceso a plugins
    /// The path.
    public CatalogLoader(string path)
    {
    // Verifica existencia de carpeta, sino la crea
    FileHelper.FolderVerifyCreate(path);

    var directoryCatalog = new DirectoryCatalog(path);

    //An aggregate catalog that combines multiple catalogs
    var catalog = new AggregateCatalog(directoryCatalog);

    // Create the CompositionContainer with all parts in the catalog (links Exports and Imports)
    var container = new CompositionContainer(catalog);

    //Carga la propiedad Plugins con los elementos detectados, pero como Plugins es Lazy no los instancia
    container.ComposeParts(this);
    }
    #endregion
    }

Leave a Reply

Your email address will not be published. Required fields are marked *