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

时间:2015-08-19 15:01:50

标签: c# visual-studio-2013 plugins mef recursive-datastructures

我正在尝试创建一个(我的第一个MEF)系统,其中插件可以递归,即我的主系统调用带有标准接口的MEF插件,然后它可以调用另一个(或几个)插件(s) ),等等。

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

知道如何防止这种情况吗?

接口:

var even = function(num) {
    return (num === 0) || !(even(num -1));
}

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

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

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

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

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

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

但是,我的主插件中的“运行”方法不会运行子插件代码,而是激活自身。

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

作为MEF的新手,我很困惑如何解决这个问题。任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

您应该使用Contracts并指定您的意图,否则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

希望这有助于......:)