我正在使用以下命令创建 WPF MVVM 应用程序简易注射器作为 DI 容器。现在,当我尝试从简单注入器解析视图时遇到一些问题,因为我需要在构造时将参数传递到构造函数中(而不是在将视图注册到容器时,因此这不是适用的:简单注入器将值传递到构造函数中 https://stackoverflow.com/q/13747027/521773).
我所追求的是这样的:
var item = container.GetInstance<MyType>(myParameter);
我读过几个地方,这在简单注入器中是不可能的,因为它不应该完成(包括这里:https://simpleinjector.codeplex.com/discussions/397080 https://simpleinjector.codeplex.com/discussions/397080).
这是真的吗?如果是的话,我该怎么做呢?
背景资料
我有多个视图模型和通过特定键查找的模型的集合,并且我要传递到视图中的参数是视图模型要使用的键。我发现这是必要的,因为视图模型和模型在应用程序的多个位置使用,并且如果它们具有相同的密钥,则需要保持同步/是相同的实例。我认为我无法使用生命周期范围来解决这个问题,并且当我注册到容器时我无法知道密钥。我也了解到ViewModelLocator
方法可能是 ServiceLocator(反?)模式,但是,目前它是我所拥有的最好的方法。
我的构造函数当前如下所示,我希望在传入密钥时解析 IViewModelLocator:
public FillPropertiesView(IViewModelLocator vml, object key)
{
// .. Removed code
// Assign the view model
var viewModel = vml.GetViewModel<FillPropertiesViewModel>(key);
DataContext = viewModel;
}
The IViewModelLocator
如下所示(模型也存在类似的界面)。
public interface IViewModelLocator
{
// Gets the view model associated with a key, or a default
// view model if no key is supplied
T GetViewModel<T>(object key = null) where T : class, IViewModel;
}
现在我有以下问题:
- 使用视图模型键解析视图的最佳方法是什么?
- 我是否需要进行一些重构才能实现此功能?
- 自从我创建了自己的基于字典以来,我是否错过了 DI 容器的一些功能
ViewModelLocator
?
扩展信息
我在上面展示了 ViewModelLocator,我使用它的原因是为了保持可混合性(基本上它只是在 Blend 中打开时为我提供设计时数据)。如果我不必同时打开不同的窗口(请参阅下一段),则运行时视图模型对于视图的每个实例都可以相同(不依赖于键)。然而,当 ViewModel 获取模型并且 ModelLocator 时,上述问题是相同的needs用于获取现有模型(如果存在)的键。
这是针对 PowerPoint 的 VSTO 应用程序的一部分(影响设计的某些部分)。当选择屏幕上的对象时,将打开一个任务面板(这基本上是填充属性视图上面解释过)。所选对象具有提供给视图的键,以便视图从 ViewModelLocator 中提取正确的视图模型。然后,视图模型将使用 IModelLocator(类似于 IViewModelLocator)和相同的键来获取对模型的引用。同时,控制器将使用相同的密钥从 ModelLocator 获取模型。控制器侦听来自模型的更改事件并更新屏幕上的对象。每当选择新对象时,就会复制相同的过程,同时可以打开多个窗口,同时与相同或不同的对象进行交互(即,多个任务窗格都具有唯一的视图模型)。
到目前为止,我已经使用默认的无参数构造函数解析了视图,然后通过方法调用注入了视图模型:
// Sets the view model on a view
public void SetViewModel(IViewModelLocator vml, object key)
{
// Assign the view model
_viewModel = vml.GetViewModel<FillPropertiesViewModel>(key);
DataContext = _viewModel;
}
我从来不需要向容器注册视图,而是像这样解决了具体类型:
string key = "testkey" // read from the selected object
var view = container.GetInstance<FillPropertiesView>();
var vml = container.GetInstance<IViewModelLocator>();
view.SetViewModel(vml, key);
当我尝试重构这个问题时,这个问题就出现了,这样我就不必每次都调用 SetViewModel() 方法并手动解析视图模型等。当我还必须在视图模型中进行手动启动来启动时,它变得非常混乱以同样的方式建立模型。
视图模型定位器
ViewModelLocator 目前作为 DI 容器的包装器工作,即视图模型在 Simple Injector 中注册。
注册如下(在名为 CompositionHost 的类中):
container.RegisterSingle<IViewModelLocator, ViewModelLocator>();
container.RegisterSingle<IModelLocator, ModelLocator>();
实现如下所示:
// Base implementation used by ViewModelLocator and ModelLocator
public class ServiceLocator<TService> where TService : class
{
private readonly Dictionary<CombinedTypeKey, TService> _instances =
new Dictionary<CombinedTypeKey, TService>();
// Gets a service instance based on the type and a key.
// The key makes it possible to have multiple versions of the same service.
public T GetInstance<T>(object key = null) where T : class, TService
{
var combinedKey = new CombinedTypeKey(typeof(T), key);
// Check if we already have an instance
if (_instances.ContainsKey(combinedKey))
{
return _instances[combinedKey] as T;
}
// Create a new instance
// CompositionHost is a static reference to the DI container (and
// should perhaps be injected, however, that is not the main issue here)
var instance = CompositionHost.GetInstance<T>();
_instances.Add(combinedKey, instance);
return instance;
}
// A combined key to ease dictionary operations
private struct CombinedTypeKey
{
private readonly object _key;
private readonly Type _type;
public CombinedTypeKey(Type type, object key)
{
_type = type;
_key = key;
}
// Equals and GetHashCode() are overridden
}
}
public class ViewModelLocator : IViewModelLocator
{
private readonly ServiceLocator<IViewModel> _viewModelLocator;
public ViewModelLocator(ServiceLocator<IViewModel> locator)
{
_viewModelLocator = locator;
// Dummy code that registers design time data is removed
}
// IViewModel is just an empty interface implemented by the view models
public T GetViewModel<T>(object key = null) where T : class, IViewModel
{
return _viewModelLocator.GetInstance<T>(key);
}
}