简体   繁体   中英

Passing parameters to plugin constructor via MEF on .NET Core?

I spent several hours trying to figure out how to pass parameters to a plugin constructor via MEF (System.Composition) but all to no avail. Needless to say, there are very few relevant docs and a look through the source code didn't help.

This used to be really easy to do, using the CompositionHost.ComposeExportedValue method, but in the .NET Core version I can't seem to find anything that works. I've enclosed my incomplete code, below, followed by the exception that is thrown.

Any help in this regard would be greatly appreciated. Thanks....

using System;
using System.Composition;
using System.Composition.Hosting;
using System.Reflection;

namespace MefMe
{
    public interface IPlugin
    {
        void Alert();
    }

    [Export(typeof(IPlugin))]
    public class Plugin : IPlugin
    {
        private string code;

        [ImportingConstructor]
        public Plugin(string code)
        {
            this.code = code;
        }

        public void Alert() => Console.WriteLine(code);
    }

    class Program
    {
        static void Main(string[] args)
        {
            var config = new ContainerConfiguration()
                .WithAssembly(Assembly.GetEntryAssembly());

            var container = config.CreateContainer();

            // Throws a CompositionFailedException; see notes
            var plugin = container.GetExport<IPlugin>();

            plugin.Alert();
        }
    }
}

System.Composition.Hosting.CompositionFailedException occurred
HResult=0x80131500 Message=No export was found for the contract 'String' -> required by import 'code' of part 'Plugin' -> required by initial request for contract 'IPlugin' Source= StackTrace: at System.Composition.Hosting.Core.ExportDescriptorRegistryUpdate.CheckTarget(CompositionDependency dependency, HashSet 1 checked, Stack 1 checking) at System.Composition.Hosting.Core.ExportDescriptorRegistryUpdate.CheckDependency(CompositionDependency dependency, HashSet 1 checked, Stack 1 checking) at System.Composition.Hosting.Core.ExportDescriptorRegistryUpdate.CheckTarget(CompositionDependency dependency, HashSet 1 checked, Stack 1 checking) at System.Composition.Hosting.Core.ExportDescriptorRegistryUpdate.Execute(CompositionContract contract) at System.Composition.Hosting.Core.ExportDescriptorRegistry.TryGetSingleForExport(CompositionContract exportKey, ExportDescriptor& defaultForExport) at System.Composition.Hosting.Core.LifetimeContext.TryGetExport(CompositionContract contract, Object& e xport) at System.Composition.CompositionContext.GetExport(CompositionContract contract) at System.Composition.CompositionContext.GetExport[TExport](String contractName) at MefMe.Program.Main(String[] args) in C:\\Users\\louis\\Desktop\\MefMe\\MefMe\\Program.cs:line 36

Unfortunately ComposeExportedValue is not currently supported. There is an open Github Issue requesting the feature.

There is a long winded workaround that will do the trick. There is no way that I can find to create exports on the fly with populated values. What you can do is create explicit property exports with the values set in the constructor via a static bag of parameters. I have updated your code snippet to show my solution.

 namespace MefMe
{
    public interface IPlugin
    {
        void Alert();
    }

    [Export( typeof( IPlugin ) )]
    public class Plugin : IPlugin
    {
        private string code;

        [ImportingConstructor]
        public Plugin( [Import( "code" )] object code )
        {
            this.code = (string)code;
        }

        public void Alert() => Console.WriteLine( code );
    }

    public class Parameters
    {
        public static IEnumerable<Tuple<string, object>> PopulatedParameters { get; set; }
        [Export( "code", typeof( object ) )]
        public object code { 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 class Program
    {           
        static void Main( string[] args )
        {
            Parameters.PopulatedParameters = new Tuple<string, object>[] { new Tuple<string, object>( "code", "myvalue" ) };

            var config = new ContainerConfiguration()
                .WithAssembly( typeof( IPlugin ).Assembly );
            var container = config.CreateContainer();

            // Throws a CompositionFailedException; see notes
            var plugin = container.GetExport<IPlugin>();

        }
    }
}

For now you need to build your own ExportDescriptorProvider. Then add it into the container config using "WithProvider". Here is an example .

(That one was almost exactly the same question.)

Don't mean to take credit for anyone else's work; I just think stackoverflow can use a LOT more material related to System.Composition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM