我一直在试验 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; }
}
- 或者,您可以利用
ExportFactory<T, TMetadata>
.它是 MEF (Silverlight) 的一部分, MEF Codeplex library (ver 2) , 和 Microsoft.Net 4.5 .请注意,ExportFactory 无法实例化类并将参数传递给构造函数,因此您不得不使用text
通过在创建实例后设置属性(或调用方法)。另外,这里有一些关于 Using 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
如果你需要的话,是值得研究的。基本上,您可以扩展此答案以自定义您的解决方案。
关于parameters - MEF+MVVM : How to instantiate a class with non-importing or mixed parameters,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14978773/