parameters - MEF+MVVM : How to instantiate a class with non-importing or mixed parameters

标签 parameters constructor viewmodel mef factory

我一直在试验 MEF 和 MVVM。我想让 MEF 使用字符串构造函数参数初始化 NonShared ViewModel 实例,即如下所示:

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

显然 MEF 不允许我这样做。

我用谷歌搜索了一下,有人说 ExportFactory 是正确的工具,但没有语法示例。我一直无法弄清楚如何使用 ExportFactory 来初始化带有构造函数参数(或者我应该说非导入参数)的实例。

然后我尝试使用 ViewModelFactory 来实现这一点。我引用了这个 article

然后想出了这样的事情:

[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 是否无法处理这种情况?

最佳答案

有两种方法可以完成您想要做的事情:

  • 创建一个工厂并将所有必要的部分导入工厂,然后将其传递给类的构造函数。为此,您必须让您的工厂了解实现 IBarViewModel 的不同类。接口(interface)和它们实例化所需的部分,或使用反射将它们放入某个列表中。

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如果你需要的话,是值得研究的。基本上,您可以扩展此答案以自定义您的解决方案。

关于parameters - MEF+MVVM : How to instantiate a class with non-importing or mixed parameters,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14978773/

相关文章:

java - 在 Java 中将变量从一个类传递到另一个类总是给我一个 null 错误

java - 为什么我收到错误 "the Method Is Undefined for the type"?

android - Kotlin - 具有不同参数的类辅助构造函数

c++ - STL vector 和 c++ : how to . 在没有默认构造函数的情况下调整大小?

android - DAO 方法返回值出现空指针异常

css - 根据来自 Controller 的逻辑在 MVC 中设置 View 样式

c++ - 为什么这是一个传递指针函数?

parameters - LIBSVM 参数选择

C++ 将枚举作为参数传递

kotlin - 找不到 ViewModelProviders 类,只有 ViewModelProvider