我一直在试验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)从其他地方导入一个Container(2)新的VM up with parameter(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是否无法处理这个问题?
答案 0 :(得分: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 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
可以查看。基本上,您可以扩展此答案以自定义您的解决方案。