3

私は MEF と MVVM を実験しています。MEF に、文字列コンストラクター パラメーターを使用して NonShared ViewModel インスタンスを初期化させたいと考えました。つまり、次のようなものです。

// BarViewModel's constructor has one single string parameter
IBarViewModel bar = container.GetExportedValue<IBarViewModel>("bar title");

明らかに、MEF は私にこれをさせませんでした。

私はグーグルで検索しましたが、ExportFactory がこれに適したツールであると言う人もいますが、構文サンプルはありません。ExportFactory を使用してインスタンスをコンストラクターパラメーター (または非インポートパラメーターと言うべきか) で初期化する方法を理解できませんでした。

そこで、これを実現するために ViewModelFactory を使用しようとしました。この記事を参考にしました

そして、次のようなものを思いつきました:

[Export(typeof(IBarViewModelFactory))]
public class BarViewModelFactory : IBarViewModelFactory
{
    [Import]
    public Lazy<CompositionContainer> Container { get; set; }

    public IBarViewModel CreateBarViewModel(string text)
    {
        IBarViewModel result = null;
        var tempContainer = CreateTemporaryDisposableContainer(Container.Value);
        try
        {
            result = new BarViewModel(text);
            tempContainer.ComposeParts(result);
        }
        catch (Exception ex)
        {
        }
        finally
        {              
        }

        return result;
    }

基本的に私がここでやっていることは、(1) 別の場所からコンテナをインポートする (2) パラメータを使用して VM を新しくする (3) 一時的なコンテナを使用して、新しく作成されたインスタンスの依存関係を解決することです。

このコードは正常に動作しているように見えますが、(a) BarViewModel は [ImportingConstructor] を持つことができなくなりました (b) BarViewModel の [Import] プロパティは、ctor スコープで null であるため、コンストラクターで使用できません。

これは、ViewModel の使用が非常に制限されていることを意味し、MEF で次のようなクラスを初期化できないことも意味します。

[Import]
public ILogger Logger {get;set;}

[ImportingConstructor]
public SomeClass(IDataService service, string text)
{
    Logger.Trace(text);
}

このクラスを MEF でインスタンス化する方法がわかりません。これは非常に一般的なシナリオだと思いますが、MEF はこれを処理できないのでしょうか?

4

1 に答える 1

2

やりたいことを実行するには、次の 2 つの方法があります。

  • ファクトリを作成し、必要なすべてのパーツをファクトリにインポートしてから、それをクラスのコンストラクターに渡します。これを機能させるには、インスタンス化に必要なインターフェイスとパーツを実装するさまざまなクラスをファクトリに認識させるIBarViewModelか、リフレクションを使用してリストにそれらを取得する必要があります。

BarViewModelFactory:

[Export(typeof(IBarViewModelFactory))]
public class BarViewModelFactory : IBarViewModelFactory
{
    [Import] private IServiceA ServiceA { get; set; }
    [Import] private IServiceB ServiceB { get; set; }

    // val is some metadata value to help decide which class to instantiate.
    public IBarViewModel CreateBarViewModel(string val, string text)
    {
        IBarViewModel result = null;

        try
        {
            // Select using metadata...
            if (String.Equals(val, "x", StringComparison.OrdinalIgnoreCase))
                result = new SuperBarViewModel(ServiceA, ServiceB, text);
            else
                result = new BarViewModel(ServiceA, text);
        }
        catch (Exception ex) { ... }
        finally { ... }

        return result;
    }    
}

IBarViewModel の実装:

public sealed class BarViewModel : ViewModelBase, IBarViewModel
{
    public BarViewModel(IServiceA svcA, string text)
    {
        ServiceA = svcA;

        // Do something with text, etc...
    }

    private IServiceA ServiceA { get; set; }
}

public sealed class SuperBarViewModel : ViewModelBase, IBarViewModel
{
    public BarViewModel(IServiceA svcA, IServiceB svcB, string text)
    {
        ServiceA = svcA;
        ServiceB = svcB;

        // Do something with text, etc...
    }

    private IServiceA ServiceA { get; set; }
    private IServiceB ServiceB { get; set; }
}

  • または、 を利用できる場合もありますExportFactory<T, TMetadata>。これは、MEF (Silverlight)MEF Codeplex ライブラリ (ver 2)、およびMicrosoft.Net 4.5の一部です。ExportFactory はクラスをインスタンス化してコンストラクターにパラメーターを渡すことができないためtext、インスタンスの作成後にプロパティを設定する (またはメソッドを呼び出す) ことによって を使用する必要があることに注意してください。また、ExportMetadataAttribute の使用に関する情報もここにあります。

BarViewModelFactory:

[Export(typeof(IBarViewModelFactory))]
public class BarViewModelFactory : IBarViewModelFactory
{
    [ImportMany]
    private IEnumerable<ExportFactory<IBarViewModel, IBarViewModelMetadata>> Factories { get; set; }

    // val is some metadata value to help decide which class to return.
    public IBarViewModel CreateBarViewModel(string val, string text)
    {
        IBarViewModel result = null;

        try
        {
            result = Factories.Single(x => String.Equals(x.Metadata.Value, val, StringComparison.OrdinalIgnoreCase))
                              .CreateExport()
                              .Value;

            result.Text = text;
        }
        catch (Exception ex) { ... }
        finally { ... }

        return result;
    }
}

IBarViewModel の実装:

[ExportMetadata(typeof(IBarViewModel, ""))]
public sealed class BarViewModel : ViewModelBase, IBarViewModel
{
    [Import] private IServiceA ServiceA { get; set; }

    public string Text { get; set; }
}

[ExportMetadata(typeof(IBarViewModel, "x"))]
public sealed class SuperBarViewModel : ViewModelBase, IBarViewModel
{
    [Import] private IServiceA ServiceA { get; set; }
    [Import] private IServiceB ServiceB { get; set; }

    public string Text { get; set; }
}

SuperBarViewModel両方のシナリオで、 ("x") と("")を区別するために文字列テキストを使用することにしましたBarViewModelが、いずれかを選択するために必要なメタデータを実質的に選択できます。

使用例 (両方のシナリオで機能):

[Export]
public sealed class SomeClass
{
    [Import]
    private IBarViewModelFactory BarViewModelFactory { get; set; }

    public void SomeMethod()
    {
        // Get the "Super" version by passing in "x" (metadata).
        var barVM = BarViewModelFactory.CreateBarViewModel("x", "my text");

        // etc...
    }
}

明らかに、メタデータを含むエクスポートExportAttributeのデフォルトを使用する代わりに独自のカスタムを作成するなど、これに加えてできることは他にもたくさんあります。ExportMetadataAttributeこれにより、より複雑なメタデータを作成できます。また、SatisfyImports/SatisfyImportsOnceは必要に応じて調べる必要があります。基本的に、この回答を拡張してソリューションをカスタマイズできます。

于 2013-02-20T12:36:37.097 に答える