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

核能气质少年 提交于 2019-12-23 14:19:01

问题


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, HashSet1 checked, Stack1 checking) at System.Composition.Hosting.Core.ExportDescriptorRegistryUpdate.CheckDependency(CompositionDependency dependency, HashSet1 checked, Stack1 checking) at System.Composition.Hosting.Core.ExportDescriptorRegistryUpdate.CheckTarget(CompositionDependency dependency, HashSet1 checked, Stack1 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& export) 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


回答1:


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>();

        }
    }
}



回答2:


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.



来源:https://stackoverflow.com/questions/44596189/passing-parameters-to-plugin-constructor-via-mef-on-net-core

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!