/ .Net Standard 2.0

Porting to .Net Standard 2.0 Part 2: Porting MEF 1.0 to MEF 2.0 on .Net Core

Porting an existing MEF 1.0 .Net 4.0 implementation to MEF 2.0 on .Net Core

In Part 1 I discussed the basic setup of porting an existing .Net 4.0 code base over to a .Net Standard 2.0 project. In this part I am going to discuss the specifics of porting code using MEF (Microsoft Extensibility Framework) to .Net Core.

MEF 1.0 was introduced in .Net 4.0 as part of the full framework. In .Net 4.5, MEF 1.0 was dropped from the full framework, to be replcaed by MEF 2.0 delivered via the Microsoft.Composition NuGet Package. MEF 1.0 has not been ported to .Net Core, although there is an open Github Issue requesting a full port. MEF 2.0 has been fully ported and renamed to System.Composition. The full source code is available on the .Net Core Github page.

Our extensible licensing engine was written against .Net 4.0 and is heavily reliant on MEF for loading our secure execution engine and plugins for various license store types. The significant differences between MEF 1.0 and MEF 2.0 have caused more than a few headaches in figuring out both equivalent functionality and workarounds for areas that are completely missing. The most significant changes that impacted us are the removal of the DirectoryCatalog, ImportDefinition and ComposeExportedValues. The first two have obvious enough ways to work around, the last one proved signifcantly trickier.

While the basic functionality of static type resolution is still supported the APIs have changed quite a bit.

Example: .Net 4.0 System.ComponentModel.Composition

  var container = new CompositionContainer( new AssemblyCatalog( typeof( MyType ).Assembly ) );
  var resolvedType = container.GetExportedValue<MyTypeInterface>("MyContractName");

Example: .Net Core System.Composition

   var configuration = new ContainerConfiguration().WithAssembly( typeof( MyType ).Assembly ) );
   var container = configuration.CreateContainer();
   var resolvedType = container.GetExportedValue<MyTypeInterface>("MyContractName");

In MEF 1.0 it was possible using DirectoryCatalog to create an AssemblyCatalog of parts from all assemblies within a given folder. The most obvious use of this is loading assemblies sitting side by side with your running application or from a known plugins folder. No equivalent functionality exists in MEF 2.0. There is an open Github Issue requesting the feature and a simple enough workaround recommended.

Example: .Net 4.0 System.ComponentModel.Composition

    var directory = AppDomain.CurrentDomain.BaseDirectory;
    var catalog = new DirectoryCatalog( directory );
    var container = new CompositionContainer( catalog );

Example: .Net Core System.Composition - requires System.Runtime.Loader package

    var directory = AppDomain.CurrentDomain.BaseDirectory;
    var assemblies = Directory.GetFiles( directory, "*.dll" )
                    .Select(  AssemblyLoadContext.Default.LoadFromAssemblyPath );

    var configuration = new ContainerConfiguration().WithAssemblies( assemblies );
    var container = configuration.CreateContainer();

The next issue we encountered was the removal of ImportDefinition. This allowed us to define our own predicate matching functionality and ImportCardinality. In MEF 2.0 exports must be declared and retrieved using strong typing and optionally a contract name. There are a few overloads for retrieving exports but under the hood they are all using a CompositionContract and a quick look at the Equals function in the code base confirms that type comparison is required. This is a serious issue for our system. Due to the secure and individualized nature of the end library provided to our customers, both the assembly name and more significantly, the types, are altered and replaced as part of a build post processing step. While initially this seemed to present a significant difficulty, we soon had a lightbulb moment and realised we could achieve the same result by explicitly exporting typeof( object ).

Example: .Net 4.0 System.ComponentModel.Composition

    [Export( "IMyClass ")]
    public class MyClass : IMyClass
    {
    }

     var container = new CompositionContainer( new AssemblyCatalog( typeof( MyClass ).Assembly ) );
    var resolvedType = container.GetExports(
        new ImportDefinition(
            x => x.ContractName == typeof( IMyClass ).Name, 
            typeof( IMyClass ).Name, 
            ImportCardinality.ExactlyOne,
            false,
            true ) )
    .Select( x => x.Value )
    .Cast<T>()
    .Single();

Example: .Net Core System.Composition

    [Export( "IMyClass ", typeof( object ))]
    public class MyClass : IMyClass
    {
    }

    T GetExportByContract<T>()
    {
        var configuration = new ContainerConfiguration().WithAssembly( typeof( T ).Assembly );
        var container = configuration.CreateContainer();
        return (T)_host.Value.GetExport( new CompositionContract( typeof( object ), typeof( T ).Name ) );
    }

Finally, we come to most frustrating and head-ache filled difference for anyone who fully jumped on board the MEF 1.0 boat. That is the absence of ComposeExportedValues or any remotely equivalent functionality. ComposeExportedValues allowed you to create exports on the fly, and more importantly to inject existing instances of types into your constructors. MEF 2.0 is a fully static composition system and there is no support for injecting anything other than a type. As with previous issues there is an open Github Issue requesting this feature. However, notably absent from this one is any sensible proposal as to how to work around it. I will freely admit there was an awful lot of cursing in my office when we found this issue. While we are hopeful this functionality will be added at some future date, we couldn't wait. Without it we either have to completely redesign our system or put .Net Standard support on hold indefinitely. Happily, neither of those things are happening. We have found a workable solution, long winded and ugly as sin compared to the simplicity of the .Net 4.0 solution, but it gets us over the hump.

There is no way to create MEF exports on the fly that I have found (would be happy to be wrong on this if anyone has a suggestion). You can however declare explicit property exports to match the instances you want to import. These can be differentiated via a ContractName and instantiated via a constructor reading values from a static bag.

Example: .Net 4.0 System.ComponentModel.Composition

    public void Compose(){
        bool myValue = true;
        var container = new CompositionContainer( new AssemblyCatalog( typeof( MyService ).Assembly ) );    
        container.ComposeExportedValue( "MyContract", myvalue );
    }

    [Export( "IMyService", typeof (obejct ) )]
    public class MyService : IMyService
    {
        [ImportingConstructor]
        public MyService([Import( "MyContract" )] object myparam)
        {
            var value = (bool)myparam;
        }
    }
    

Example: .Net Core System.Composition

    public class Parameters
    {
        public static IEnumerable<Tuple<string, object>> PopulatedParameters { get; set; }
        [Export( "myparam", typeof( object ) )]
        public object myparam { get; set; }

        public Parameters()
        {
            foreach (var param in PopulatedParameters)
                SetParameter( param.Item1, param.Item2 );
        }

        void SetParameter( string nameOfParam, object value )
        {
            var property = typeof( Parameters ).GetProperty( nameOfParam, BindingFlags.Public | BindingFlags.Instance );
            property.SetValue( this, value );
        }
    }

    public void Compose(IEnumberable<Tuple<string, object>> parameters)
    {
        Parameters.PopulatedParameters = parameters;
        var configuration = new ContainerConfiguration().WithAssembly( typeof( T ).Assembly );
        var container = configuration.CreateContainer();
    }

    [Export( "IMyService", typeof (obejct ) )]
    public class MyService : IMyService
    {
        [ImportingConstructor]
        public MyService([Import( "MyContract" )] object myparam)
        {
            var value = (bool)myparam;
        }
    }   

This is obviously not ideal as you need to declare an explicit property export for each parameter instance you want to import. However, for us at least, it does the trick for now and allows us to feed in a varying subset of the known values.

In the next post I will discuss moving from .Net 4.0 System.Web.Services to System.ServiceModel in .Net Core.

Siobhan Connell

Technical Architect with Inish Technology Ventures, specializing in .Net Licensing and Code Protection.

Read More