MVVM:绑定到Model,同时保持Model与服务器版本同步

nds*_*dsc 28 c# silverlight domain-driven-design prism mvvm

我花了很多时间尝试为以下挑战找到一个优雅的解决方案.我一直无法找到解决问题的解决方案.

我有一个简单的View,ViewModel和Model设置.为了便于解释,我会保持简单.

  • Model有一个名为TitleString类型的属性.
  • Model是在DataContext的View.
  • ViewTextBlock这就是数据绑定到Title的模型.
  • ViewModel有一个名为方法Save(),将保存ModelServer
  • Server可推到所做的更改Model

到现在为止还挺好.现在我需要进行两项调整才能使模型与a保持同步Server.服务器的类型并不重要.只要知道我需要调用Save()才能将模型推送到Server.

调整1:

  • Model.Title属性将需要调用RaisePropertyChanged(),以转换为所做的更改ModelServerView.这很好用,因为它Model是DataContextView

还不错.

调整2:

  • 下一步是调用Save()保存从所做的更改ViewModelServer.这是我被卡住的地方.当模型被更改时,我可以处理调用Save()的Model.PropertyChanged事件,ViewModel但这会使它回显服务器所做的更改.

我正在寻找一个优雅而合理的解决方案,如果有意义,我愿意改变我的架构.

Jon*_*Jon 69

在过去,我编写了一个支持从多个位置"实时"编辑数据对象的应用程序:应用程序的许多实例可以同时编辑同一个对象,当有人将更改推送到服务器时,其他所有人都会得到通知, (在最简单的场景中)立即看到这些变化.以下是它的设计总结.

建立

  1. 视图始终绑定到ViewModels.我知道这是很多样板,但直接绑定到模型是不可接受的,除了最简单的场景; 它也不符合MVVM的精神.

  2. ViewModels 全权负责推动变革.这显然包括推动对服务器的更改,但它也可能包括将更改推送到应用程序的其他组件.

    要做到这一点,ViewModels可能希望克隆它们包装的模型,以便它们可以向应用程序的其余部分提供事务语义(即,您可以选择何时将更改推送到应用程序的其余部分,即您如果每个人都直接绑定到同一个Model实例,则无法执行此操作.隔离这样的改变需要还是更多的工作,但它也开辟了强大的可能性(例如,撤消更改很简单:就是不把他们).

  3. ViewModels依赖于某种数据服务.数据服务是位于数据存储和使用者之间的应用程序组件,用于处理它们之间的所有通信.每当ViewModel克隆其模型时,它还会订阅数据服务公开的相应"数据存储已更改"事件.

    这允许ViewModels通知其他ViewModel已推送到数据存储并做出适当反应的"他们的"模型的更改.通过适当的抽象,数据存储也可以是任何东西(例如,该特定应用程序中的WCF服务).

工作流程

  1. 创建ViewModel并为其分配Model的所有权.它立即克隆模型并将此克隆公开给View.它依赖于数据服务,它告诉DS它想要订阅更新此特定模型的通知.ViewModel不知道识别其模型("主键")是什么,但它不需要,因为这是DS的责任.

  2. 当用户完成编辑时,他们与View进行交互,后者在VM上调用Command.然后,VM调用DS,推送对其克隆模型所做的更改.

  3. DS会保留更改并另外引发一个事件,通知所有其他感兴趣的VM已经对模型X进行了更改; 模型的新版本作为事件参数的一部分提供.

  4. 已分配相同模型所有权的其他VM现在知道外部更改已到达.他们现在可以决定如何更新包含手头所有拼图的视图(模型的"先前"版本,克隆;"脏"版本,克隆;以及"当前"版本,作为事件参数的一部分被推动了).

笔记

  • 模型INotifyPropertyChanged仅由View使用; 如果ViewModel想要知道模型是否"脏",它总是可以将克隆与原始版本进行比较(如果它已被保留,如果可能的话,我建议这样做).
  • ViewModel以原子方式将更改推送到Server,这很好,因为它可确保数据存储始终处于一致状态.这是一种设计选择,如果你想以不同的方式做事,另一种设计会更合适.
  • 如果ViewModel this作为参数传递给"推送更改"调用,则服务器可以选择不为引发此更改的ViewModel引发"模型已更改"事件.即使它没有,ViewModel也可以选择不做任何事情,如果它看到模型的"当前"版本与其自己的克隆相同.
  • 通过足够的抽象,可以将更改推送到在其他计算机上运行的其他进程,就像将它们推送到shell中的其他视图一样容易.

希望这可以帮助; 如果需要,我可以提供更多说明.

  • +1用于简短明确地阐述MVVM基础知识. (2认同)

Gon*_*ing 7

我建议将控制器添加到MVVM混合(MVCVM?)以简化更新模式.

控制器侦听更高级别的更改,并在Model和ViewModel之间传播更改.

保持清洁的基本规则是:

  • ViewModels只是具有特定形状数据的哑容器.他们不知道数据来自何处或显示在何处.
  • 视图显示特定形状的数据(通过绑定到视图模型). 他们不知道数据来自何处,只知道如何显示数据.
  • 模型提供真实数据.他们不知道它的消费地点.
  • 控制器实现逻辑.比如在VM中提供ICommands代码,监听数据更改等等.它们从模型中填充VM.让他们监听VM更改并更新Model是有意义的.

正如在另一个答案中提到的,你DataContext应该是VM(或它的属性),而不是模型.指向DataModel使得很难分离关注点(例如,用于测试驱动开发).

大多数其他解决方案将逻辑放在ViewModels中,这是"不对",但我发现控制器的好处一直被忽视.认为MVVM的缩写!:)