0

正如这些 问题中所解释的,我正在尝试构建一个由主机和多个任务处理客户端组成的应用程序。在一些帮助下,我弄清楚了如何发现和序列化部件定义,以便我可以存储这些定义而不必加载实际的运行时类型。

我想要实现的下一步(或者实际上是接下来的两个步骤)是我想将部件的组合与对象的实际创建和连接(由这些部件表示)分开。因此,如果我有一组部件,那么我希望能够执行以下操作(在伪代码中):

public sealed class Host
{
    public CreationScript Compose()
    {
        CreationScript result;
        var container = new DelayLoadCompositionContainer(
            s => result = s);
        container.Compose();
        return script;
    }

    public static void Main()
    {
        var script = Compose();

        // Send the script to the client application
        SendToClient(script);
    }
}

// Lives inside other application
public sealed class Client
{
    public void Load(CreationScript script)
    {
        var container = new ScriptLoader(script);
        container.Load();
    }

    public static void Main(string scriptText)
    {
        var script = new CreationScript(scriptText);
        Load(script);
    }
}

这样我就可以在主机应用程序中组合部件,但实际上加载代码并在客户端应用程序中执行它。目标是将决定加载什么的所有智能都放在一个位置(主机),而实际工作可以在任何地方(由客户端)完成。

本质上,我正在寻找的是某种获取 MEF 隐式创建的 ComposablePart 图的方法。

现在我的问题是,MEF 中是否有任何位可以让我实现这种行为?我怀疑提供者模型可能会帮助我解决这个问题,但这是 MEF 相当大且复杂的部分,因此任何指南都会有所帮助。

4

1 回答 1

2

从大量调查来看,似乎不可能将 MEF 中的组合过程与实例化过程分开,因此我不得不为这个问题创建自己的方法。该解决方案假定插件的扫描导致以某种方式存储类型、导入和导出数据。

为了组合零件,您需要跟踪每个零件实例以及它如何连接到其他零件实例。最简单的方法是使用图形数据结构来跟踪哪个导入连接到哪个导出。

public sealed class CompositionCollection
{
    private readonly Dictionary<PartId, PartDefinition> m_Parts;
    private readonly Graph<PartId, PartEdge> m_PartConnections;

    public PartId Add(PartDefinition definition)
    {
        var id = new PartId();
        m_Parts.Add(id, definition);
        m_PartConnections.AddVertex(id);

        return id;
    }

    public void Connect(
        PartId importingPart, 
        MyImportDefinition import,
        PartId exportingPart,
        MyExportDefinition export)
    {
        // Assume that edges point from the export to the import
        m_PartConnections.AddEdge(
            new PartEdge(
                exportingPart,
                export,
                importingPart,
                import));
    }
}

请注意,在连接两个部分之前,有必要检查导入是否可以连接到导出。在其他情况下,MEF 会这样做,但在这种情况下,我们需要自己做。如何处理的一个例子是:

public bool Accepts(
    MyImportDefinition importDefinition, 
    MyExportDefinition exportDefinition)
{
    if (!string.Equals(
        importDefinition.ContractName, 
        exportDefinition.ContractName, 
        StringComparison.OrdinalIgnoreCase))
    {
        return false;
    }

    // Determine what the actual type is we're importing. MEF provides us with 
    // that information through the RequiredTypeIdentity property. We'll 
    // get the type identity first (e.g. System.String)
    var importRequiredType = importDefinition.RequiredTypeIdentity;

    // Once we have the type identity we need to get the type information
    // (still in serialized format of course)
    var importRequiredTypeDef = 
        m_Repository.TypeByIdentity(importRequiredType);

    // Now find the type we're exporting
    var exportType = ExportedType(exportDefinition);
    if (AvailableTypeMatchesRequiredType(importRequiredType, exportType))
    {
        return true;
    }

    // The import and export can't directly be mapped so maybe the import is a 
    // special case. Try those
    Func<TypeIdentity, TypeDefinition> toDefinition = 
        t => m_Repository.TypeByIdentity(t);
    if (ImportIsCollection(importRequiredTypeDef, toDefinition) 
        && ExportMatchesCollectionImport(
            importRequiredType, 
            exportType, 
            toDefinition))
    {
        return true;
    }

    if (ImportIsLazy(importRequiredTypeDef, toDefinition) 
        && ExportMatchesLazyImport(importRequiredType, exportType))
    {
        return true;
    }

    if (ImportIsFunc(importRequiredTypeDef, toDefinition) 
        && ExportMatchesFuncImport(
            importRequiredType, 
            exportType, 
            exportDefinition))
    {
        return true;
    }

    if (ImportIsAction(importRequiredTypeDef, toDefinition) 
        && ExportMatchesActionImport(importRequiredType, exportDefinition))
    {
        return true;
    }

    return false;
}

请注意,特殊情况(例如IEnumerable<T>Lazy<T>)需要确定导入类型是否基于泛型类型,这可能有点棘手。

一旦存储了所有组成信息,就可以在任何时间点对零件进行实例化,因为所有必需的信息都可用。实例化需要大量的反射帮助以及可靠的Activator类的使用,并将作为练习留给读者。

于 2012-12-13T02:59:34.177 回答