Search code examples
c#composition.net-standard-2.0mef2

MEF2 Lightweight System.Composition with Metadata


The complete lack of examples for how to use lightweight MEF2, System.Composition, makes this tricky. I am only using System.Composition (not System.ComponentModel.Composition).

I want to import parts that have metadata. I'm using attributed code. Unfortunately when I try to get the exports I get a big fat null.

The MetadataAttribute:

namespace Test.ConfigurationReaders
{
    using System;
    using System.Composition;

    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class ExportReaderAttribute : ExportAttribute
    {
        public string Reader { get; set; }
    }
}

Export:

namespace Test.ConfigurationReaders
{
    using System.Collections.Generic;
    using System.Configuration;

    [ExportReader(Reader = "CONFIG")]
    public class ConfigReader : IConfigurationReader
    {
        public IEnumerable<Feature> ReadAll()
        {
            return new List<Feature>();
        }
    }
}

ImportMany and filter on metadata:

namespace Test.Core
{
    using System;
    using System.Collections.Generic;
    using System.Composition;
    using System.Composition.Hosting;
    using System.Configuration;
    using System.Linq;
    using System.Reflection;
    using ConfigurationReaders;

    public sealed class Test
    {
        static Test()
        {
            new Test();
        }

        private Test()
        {
            SetImports();
            Reader = SetReader();
        }

        [ImportMany]
        private static IEnumerable<ExportFactory<IConfigurationReader, ExportReaderAttribute>> Readers { get; set; }

        public static IConfigurationReader Reader { get; private set; }

        private static IConfigurationReader SetReader()
        {
            // set the configuation reader based on an AppSetting.
            var source =
                ConfigurationManager.AppSettings["Source"]
                ?? "CONFIG";

            var readers = Readers.ToList();

            var reader =
                Readers.ToList()
                .Find(
                    f =>
                    f.Metadata.Reader.Equals(
                        source,
                        StringComparison.OrdinalIgnoreCase))
                    .CreateExport()
                    .Value;

            return reader;
        }

        private void SetImports()
        {
            var configuration =
                new ContainerConfiguration().WithAssemblies(
                    new List<Assembly> {
                        typeof(IConfigurationReader).Assembly });
            var container = configuration.CreateContainer();
            container.SatisfyImports(this);
            Readers = container.GetExports<ExportFactory<IConfigurationReader, ExportReaderAttribute>>();
        }
    }
}

Readers is, unfortunately, null. This is example code here but I can see parts that don't have metadata in my actual code so that at least is working.

What should I do to populate Readers?

I'm trying to target .NET Standard 2.0 and use it from .NET Core.


Solution

  • The solution is to change the attributes for the exported class:

        [Export(typeof(IConfigurationReader))]
        [ExportMetadata("Reader", "CONFIG")]
        public class ConfigReader : IConfigurationReader
        {
        }