MEF插件调用具有相同接口的另一个插件

本文关键字:插件 接口 另一个 调用 MEF | 更新日期: 2023-09-27 18:08:56

我正在尝试制作一个(我的第一个MEF)系统,其中插件可以递归,即我的主系统调用具有标准接口的MEF插件,其本身可以调用另一个(或几个)插件,等等。

当测试时,我的插件不调用底层插件,而是开始处理自己(创建一个循环)。

你知道我该怎么做吗?

接口:

public interface IConnector
{
    XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys);
}

我的主插件继承了这个接口,并定义了next的导入(子插件有相同的定义):

[Export(typeof(IConnector))]
public class Connector : IConnector
{
    [Import(typeof(IConnector))]
    private IConnector connector;
    ....

被调用的插件被启动(在主插件的Run方法中):

public XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys)
{
    string calledConnector = Path.Combine(AssemblyDirectory, "subplugin.dll");
    AssemblyCatalog assembyCatalog = new AssemblyCatalog(Assembly.LoadFrom(calledConnector));
    CompositionContainer container = new CompositionContainer(assembyCatalog);
    container.ComposeParts(this);
    ....

容器现在应该只包含一个插件,即subplugin.dll。我调用接口中的方法'Run'来调用子插件方法:

XDocument something = connector.Run(serviceCredentials, connectorids, connectorkeys);

但是,不是运行子插件代码,而是激活主插件中的'Run'方法,它会一直激活自己。

当我在主插件中删除[Export(typeof(iConnector)]时,子插件被激活,但我希望我的主插件能够以相同的方式调用。

作为MEF的新手,我被困在如何解决这个问题上。任何帮助将非常感激!

MEF插件调用具有相同接口的另一个插件

你应该使用契约并指定你的意图,否则MEF将进入无限循环或选择连接器,因为它暴露了IConnector本身。

来自MSDN的更多信息。

例如

[Export("Container", typeof(IConnector))]
public class Connector : IConnector
{
    [Import("Component", typeof(IConnector))]
    private IConnector connector;
    ....

因此,经过一些思考之后,这里有一个基于元数据的方法的示例,它也限制了昂贵的目录操作的数量。

IConnector

using System.Xml.Linq;
namespace Common
{
    public interface IConnector
    {
        XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys);
        void Identify();
    }
}

元数据属性ConnectorMetadata

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
namespace Common
{
    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class)]
    public class ConnectorMetadata : ExportAttribute
    {
        public string Name { get; private set; }
        public ConnectorMetadata(string name):base(typeof(IConnector))
        {
            Name = name;
        }
        public ConnectorMetadata(IDictionary<string, object> metaData) : base(typeof (IConnector))
        {
            Name = Convert.ToString(metaData["Name"]);
        }
    }
}

PluginsCatalog的惰性单例

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Reflection;
using Common;
namespace Common
{
    public class PluginsCatalog
    {
        [ImportMany]
        public Lazy<IConnector, ConnectorMetadata>[] Connectors;
        private static readonly Lazy<PluginsCatalog> LazyInstance = new Lazy<PluginsCatalog>(() => new PluginsCatalog());
        private PluginsCatalog()
        {
            var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Directory.GetCurrentDirectory();
            var directoryCatalog = new DirectoryCatalog(path, "*plugin.dll");
            var aggregateCatalog = new AggregateCatalog(assemblyCatalog, directoryCatalog);
            var container = new CompositionContainer(aggregateCatalog);
            container.SatisfyImportsOnce(this);
        }
        public static PluginsCatalog Instance { get { return LazyInstance.Value; } }
        public IConnector GetConnector(string name)
        {
            var match = Connectors.SingleOrDefault(s => s.Metadata.Name.Equals(name));
            return match == null ? null : match.Value;
        }
    }
}

"主"IConnector

using System;
using System.Xml.Linq;
using Common;
namespace Common
{
    [ConnectorMetadata("Primary")]
    public class Connector : IConnector
    {
        public XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys)
        {
            PluginsCatalog.Instance.GetConnector("Sub").Identify();
            return default(XDocument);
        }
        public void Identify()
        {
            Console.WriteLine(GetType().FullName);
        }
    }
}

"Sub" IConnector

using System;
using System.Xml.Linq;
using Common;
namespace SubPlugin
{
    [ConnectorMetadata("Sub")]
    public class SubConnector:IConnector
    {
        public XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys)
        {
            return default(XDocument);
        }
        public void Identify()
        {
            Console.WriteLine(GetType().FullName);
        }
    }
}

最后是程序本身:

namespace SOMEF
{
    class Program
    {
        static void Main(string[] args)
        {
            var connector = PluginsCatalog.Instance.GetConnector("Primary");
            connector.Identify();
            connector.Run(null, null, null);
        }
    }
}

打印:

SOMEF.Connector
SubPlugin.SubConnector

希望这有助于…:)

您可能想要阅读此https://msdn.microsoft.com/en-us/library/ee155691(v=vs.110).aspx

说明如何使用命名导出

public class MyClass
{
    [Import("MajorRevision")]
    public int MajorRevision { get; set; }
}
public class MyExportClass
{ 
    [Export("MajorRevision")] //This one will match.
    public int MajorRevision = 4;
    [Export("MinorRevision")]
    public int MinorRevision = 16;
}