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 范围内为空。

这意味着 ViewModel 的使用非常有限,也意味着无法使用 MEF 初始化这样的类:

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

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

现在我不知道如何用 MEF 实例化这个类。我想这是一个很常见的场景,所以我想知道 MEF 是否无法处理这个问题?

4

1 回答 1

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

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 回答