MVVM和IOC:处理视图模型';s类不变量

本文关键字:不变量 模型 IOC 处理 视图 MVVM | 更新日期: 2023-09-27 17:58:39

自从我开始使用MVVM以来,这是一个我一直在努力解决的问题,首先是在WPF中,现在是在Silverlight中。

我使用IOC容器来管理视图和ViewModels的分辨率。视图往往是非常基本的,有一个默认的构造函数,但ViewModels倾向于访问真实的服务,所有这些都是构建所必需的。同样,我使用IOC容器来解决问题,所以注入服务不是问题。

问题是使用IOC将所需数据传递给ViewModel。作为一个简单的例子,考虑一个允许编辑客户的屏幕。除了可能需要的任何服务外,此屏幕的ViewModel还需要一个用于显示/编辑客户数据的客户对象。

在进行任何类型的(非MVVM)库开发时,我认为通过构造函数传递类不变量是一条不可理解的规则。如果我需要类构建时间的上下文特定数据,那么所讨论的类是容器管理的,我倾向于使用抽象工厂*作为桥梁。在MVVM中,这似乎有些过头了,因为大多数ViewModel都需要自己的工厂。

我尝试/考虑过的其他几种方法包括:(1)一种初始化/加载方法,在该方法中我传递数据,这违反了通过构造函数强制类不变量的规则;(2)将数据作为参数重写(Unity)通过容器传递;(3)将数据通过全局状态包(ugh)传递。

将特定于上下文的数据从一个ViewModel传递到下一个ViewModel的替代方法是什么?是否有任何MVVM框架解决了这个特定的问题?

*,它可能有自己的问题,比如需要在调用Container.Revolve()或不管理ViewModel容器之间进行选择。温莎城堡对此有一个很好的解决方案,但AFAIK没有其他框架

编辑:

我忘了添加:如果您正在执行"View First"MVVM,我列出的一些选项甚至是不可能的,除非您先将数据传递给View,然后再传递给ViewModel。

MVVM和IOC:处理视图模型';s类不变量

我不太确定问题是什么,所以我将使用一个简单而做作的例子。

假设您有一个CustomerListViewModel,它列出了每个客户的摘要。选择客户时,您希望显示CustomerDetailViewModel。这可以采用客户ID,也可以采用ICustomer类型,该类型以前在CustomerListViewModel中填充了客户详细信息(例如,取决于您希望何时加载数据)。

我想你要问的是,如果CustomerDetailViewModel也将一系列服务作为依赖项,你想通过容器解决这些依赖项(通常用于依赖链),会发生什么。

当您首先执行视图模型时,您需要从CustomerListViewModel实例化CustomerDetailViewModel,并且您希望通过容器进行实例化,以便适当地注入依赖关系。

因此,正如您所提到的,您通常会通过一个抽象的工厂模式来实现这一点,例如ICustomerDetailViewModelFactory,它作为服务传递给CustomerListViewModel

此工厂类型具有ICustomerDetailViewModel GetCustomerDetailViewModel(ICustomer customer)方法。此工厂类型将需要对IoC容器的引用。

GetCustomerDetailViewModel工厂方法中解析ICustomerDetailViewModel时,可以指定在容器上调用Resolve时希望用于ICustomer构造函数参数的值。

例如,Unity有参数覆盖,请参阅此处获得Castle Windsor支持。Castle Windsor还有一个类型化的工厂设施,因此您不需要实现具体的工厂类型,只需要实现抽象。

所以我会选择选项2!我们使用Caliburn.Micro,它解决了很多MVVM问题,但我不知道有什么框架可以解决这个问题。

我过去经常在这个问题上挣扎。据我所知,没有其他可行的办法;你似乎已经自己深入思考了这件事。我只想两个加上我的两个0.5美分的原因为什么我经常选择选项(1):

  1. init方法比任何其他选项都更容易实现(Windsor的Typed Factory也同样容易)
  2. 没有构造函数参数的设计弱点可以减轻,在VM生命周期的后期强制检查初始化参数
  3. 调用init方法的"位置"与调用构造函数(或抽象工厂)的位置相同
  4. 与抽象工厂不同,您可以在特定接口中考虑init方法,以便处理不同继承路径上的几个VM(如果需要)
  5. 这是一个公平的折衷方案(至少在这种情况下):如果你真的无法接受它,那么就选择工厂解决方案,而不关心(非常少的)复杂性开销

我不太确定MVVM和IoC是否适合在构造函数中使用类不变量。根据我的经验,ViewModels是作为ICommand的结果创建的。执行,这允许一个简单的两阶段过程:

var vm = Container.Resolve<CustomerViewModel>();
vm.Model = CustomerRepository.GetCustomerModel(id);

在这个阶段,我的视图对ViewModel一无所知,只有当我将ViewModel注入绑定到视图的任何容器时,才会发生这种情况。我还使用DataTemplates来渲染ViewModel,这意味着我不必直接实例化视图来提供DataContext。

所以,为了回答,我会使用(1),并打破"规则"。