gpt4 book ai didi

c# - 在Simple Injector中使用自定义参数解析类

转载 作者:行者123 更新时间:2023-11-30 16:10:28 26 4
gpt4 key购买 nike

我正在使用Simple Injector作为DI容器创建WPF MVVM应用程序。现在,当我尝试从Simple Injector解析视图时遇到了一些问题,因为我需要在构造时将参数传递到构造函数中(而不是在将视图注册到容器时,因此这不是)适用:Simple Injector pass values into constructor)。

我所追求的是这样的:

var item = container.GetInstance<MyType>(myParameter);


我读过很多地方,这在Simple Injector中是不可能的,因为不应该这样做(包括这里: https://simpleinjector.codeplex.com/discussions/397080)。

这是真的吗?如果是这样,我该怎么做呢?



背景资料

我有多个视图模型和模型的集合,这些模型由一个特定的键查找,并且要传递给视图的参数是视图模型要使用的键。我发现这是必要的,因为视图模型和模型在应用程序的多个位置中使用,并且需要保持同步/如果它们具有相同的键,则必须是相同的实例。我认为我无法使用生命周期作用域来解决此问题,并且在注册到容器时无法知道密钥。我还了解到 ViewModelLocator方法可能是ServiceLocator(anti-?)模式,但是,目前这是我所拥有的最好的方法。

我的构造函数当前看起来像这样,我希望在传递密钥时解析IViewModelLocator:

public FillPropertiesView(IViewModelLocator vml, object key)
{
// .. Removed code

// Assign the view model
var viewModel = vml.GetViewModel<FillPropertiesViewModel>(key);
DataContext = viewModel;
}


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


现在我有以下问题:


用视图模型键解析视图的最佳方法是什么?
我是否必须进行一些重构才能启用此功能?
自从创建了自己的基于 ViewModelLocator的字典以来,我是否会失去DI容器的某些功能?




扩展信息

我已经在上面显示了ViewModelLocator,而我使用它的原因是为了保持可混合性(基本上,在Blend中打开时,它只是为我提供了设计时数据)。如果我不必同时打开不同的窗口(请参阅下一段),则视图的每个实例的运行时视图模型可能都是相同的(不依赖于键)。但是,上述问题在ViewModel提取模型时是相同的,并且ModelLocator需要一个密钥来提取现有模型(如果存在)。

这是针对PowerPoint的VSTO应用程序的一部分(这会影响设计的某些部分)。当选择屏幕上的对象时,将打开一个任务面板(基本上是上面解释的FillPropertiesView)。所选对象具有提供给视图的键,以便视图从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

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

}

最佳答案

(几乎)绝不会将服务定位符注入您的类中,因为这不允许对依赖项和runtime dependency analysis进行编译时检查。因此,我也建议您注册所有的根类型(例如您的视图),因为否则Simple Injector处于黑暗状态,无法就可能存在的任何错误配置向您提供建议。

由于您具有始终一起缓存在一起的View + ViewModel对,但是可能依赖于多个View + ViewModel对重用的Model实例,因此我建议采用以下设计。

定义视图和视图模型的抽象:

public interface IView<TModel>
{
IViewModel<TModel> ViewModel { get; }
}

public interface IViewModel<TModel>
{
TModel Model { get; set; }
}


定义用于通过键检索/缓存视图的抽象。

public interface IViewProvider<TView, TModel> where TView : IView<TModel>
{
TView GetViewByKey(object key);
}


通过这些抽象,您的视图可以如下所示:

public class FillPropertiesView : IView<FillPropertiesModel>
{
public FillPropertiesView(FillPropertiesViewModel viewModel)
{
this.ViewModel = viewModel;
}

public IViewModel<FillPropertiesModel> ViewModel { get; private set; }
}


您的控制器可以依赖于 IViewProvider<TView, TModel>抽象,以便在有新密钥输入时可以重新加载视图:

public class FillPropertiesController : Controller
{
IViewProvider<FillPropertiesView, FillPropertiesModel> viewProvider;
FillPropertiesView view;

public FillPropertiesController(
IViewProvider<FillPropertiesView, FillPropertiesModel> provider) {
this.viewProvider = provider;
}

public void Reinitialize(object key) {
this.view = this.viewProvider.GetViewByKey(key);
}
}


IViewProvider<TView, TModel>的实现可能如下所示:

public class ViewProvider<TView, TModel> : IViewProvider<TView, TModel> 
where TView : class, IView<TModel> {
Dictionary<object, TView> views = new Dictionary<object, TView>();
Container container;
IModelProvider<TModel> modelProvider;

public ViewProvider(Container container,
IModelProvider<TModel> modelProvider) {
this.container = container;
this.modelProvider = modelProvider;
}

public TView GetViewByKey(object key) {
TView view;

if (!this.views.TryGetValue(key, out view)) {
this.views[key] = view = this.CreateView(key);
}

return view;
}

private TView CreateView(object key) {
TView view = this.container.GetInstance<TView>();
view.ViewModel.Model = this.modelProvider.GetModelByKey(key);
return view;
}
}


此实现取决于(以前未定义的) IModelProvider<TModel>抽象。这基本上是您以前的 ModelLocator,但是通过使用泛型类型,您可以使实现更加容易,因为每个TModel我们可以拥有一个这种类型的实例(对于ViewProvider也是如此),从而使您不必做任何事情使用{类型+键}组合存储元素。

您可以按以下方式注册所有这些:

Assembly asm = Assembly.GetExecutingAssembly();

container.RegisterManyForOpenGeneric(typeof(IView<>), asm);
container.RegisterManyForOpenGeneric(typeof(IViewModel<>), asm);
container.RegisterOpenGeneric(typeof(IViewProvider<,>),
typeof(ViewProvider<,>), Lifestyle.Singleton);
container.RegisterOpenGeneric(typeof(IModelProvider<>),
typeof(ModelProvider<>), Lifestyle.Singleton);

var controllers =
from type in asm.GetTypes()
where type.IsSubClassOf(typeof(Controller))
where !type.IsAbstract
select type;

controllers.ToList().ForEach(t => container.Register(t));

container.Verify();


使用 RegisterManyForOpenGeneric,您可以让Simple Injector搜索提供的程序集,以查找给定开放通用抽象的实现,然后Simple Injector将为您批量注册它们。使用 RegisterOpenGeneric,您可以指定一个开放通用的抽象,并告诉Simple Injector在请求该抽象的接近通用版本时使用哪个实现。最后一行搜索您的应用程序,以查找所有控制器类型并将其注册到系统中。

关于c# - 在Simple Injector中使用自定义参数解析类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25767693/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com