0

これらの 質問で説明したように、ホストと複数のタスク処理クライアントで構成されるアプリケーションを構築しようとしています。いくつかの助けを借りて、実際のランタイム型をロードしなくてもパーツ定義を保存できるように、パーツ定義を検出してシリアル化する方法を見つけました。

私が達成したい次のステップ (または実際には次の 2 つのステップ) は、パーツの構成を、実際の作成とオブジェクト (これらのパーツによって表される) の接続から分離したいということです。したがって、一連のパーツがある場合は、次のことを実行できるようにしたいと考えています (疑似コードで)。

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

このようにして、ホスト アプリケーションでパーツを作成し、実際にはコードをロードしてクライアント アプリケーションで実行することができます。目標は、実際の作業はどこでも (クライアントによって) 実行できる一方で、1 つの場所 (ホスト) に何をロードするかを決定するすべてのスマートさを配置することです。

基本的に私が探しているのは、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));
    }
}

2 つのパーツを接続する前に、インポートをエクスポートに接続できるかどうかを確認する必要があることに注意してください。それ以外の場合は 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 に答える