使用MEF组合零件但推迟零件的创建

时间:2012-10-24 21:54:58

标签: lazy-loading mef

正如these questions中所述,我正在尝试构建一个由主机和多个任务处理客户端组成的应用程序。在一些帮助下,我已经找到了如何发现和序列化部件定义,以便我可以存储这些定义而无需加载实际的运行时类型。

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

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中是否有任何位可以让我实现这种行为?我怀疑provider model可能对我有所帮助,但这是MEF的一个相当大而复杂的部分,所以任何指导都会有所帮助。

1 个答案:

答案 0 :(得分:2)

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

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

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课程的使用,并留给读者作为练习。