MVVM:绑定到模型,同时保持模型与服务器版本同步 [英] MVVM: Binding to Model while keeping Model in sync with a server version
问题描述
我花了相当多的时间来尝试为以下挑战找到一个优雅的解决方案.我一直无法找到解决问题的解决方案.
I've spent quite some time to try and find an elegant solution for the following challenge. I've been unable to find a solution that's more than a hack around the problem.
我有一个简单的视图、视图模型和模型设置.为了解释起见,我将保持非常简单.
I've got a simple setup of a View, ViewModel and a Model. I will keep it very simple for the sake of explanation.
Model
有一个名为Title
的 String 类型的属性.Model
是View
的 DataContext.View
有一个TextBlock
数据绑定到模型上的Title
.ViewModel
有一个名为Save()
的方法,它将把Model
保存到一个Server
莉>Server
可以推送对Model
所做的更改
- The
Model
has a single property calledTitle
of type String. - The
Model
is the DataContext for theView
. - The
View
has aTextBlock
thats databound toTitle
on the Model. - The
ViewModel
has a method calledSave()
that will save theModel
to aServer
- The
Server
can push changes made to theModel
到目前为止一切顺利.现在我需要进行两项调整,以使模型与 Server
保持同步.服务器的类型并不重要.只知道我需要调用 Save()
以便将模型推送到 Server.
So far so good. Now there are two adjustments I need to make in order to keep the Model in sync with a Server
. The type of server is not important. Just know that I need to call Save()
in order to push the Model to the Server.
调整 1:
Model.Title
属性将需要调用RaisePropertyChanged()
以转换对
到Model
所做的更改服务器View
.这很好用,因为Model
是View
的 DataContext
- The
Model.Title
property will need to callRaisePropertyChanged()
in order to translate changes made to theModel
by theServer
to theView
. This works nicely since theModel
is the DataContext for theView
还不错.
调整 2:
- 下一步是调用
Save()
以保存从View
到Server
上的Model
所做的更改代码>.这就是我卡住的地方.我可以处理ViewModel
上的Model.PropertyChanged
事件,该事件在模型更改时调用 Save(),但这会使其回显服务器所做的更改.
- Next step is to call
Save()
to save changes made from theView
to theModel
on theServer
. This is where I get stuck. I can handle theModel.PropertyChanged
event on theViewModel
that calls Save() when the Model gets changed but this makes it echo changes made by the Server.
我正在寻找一种优雅且合乎逻辑的解决方案,并且愿意在合理的情况下更改我的架构.
I'm looking for an elegant and logical solution and am willing to change my architecture if it makes sense.
推荐答案
过去我写过一个支持live"的应用程序.从多个位置编辑数据对象:应用程序的多个实例可以同时编辑同一个对象,当有人将更改推送到服务器时,其他所有人都会收到通知,并且(在最简单的情况下)会立即看到这些更改.下面是它的设计摘要.
In the past I 've written an application that supports "live" editing of data objects from multiple locations: many instances of the app can edit the same object at the same time, and when someone pushes changes to the server everyone else gets notified and (in the simplest scenario) sees those changes immediately. Here's a summary of how it was designed.
视图总是绑定到视图模型.我知道它有很多样板文件,但在最简单的情况下,直接绑定到模型是不可接受的;这也不符合 MVVM 的精神.
Views always bind to ViewModels. I know it's a lot of boilerplate, but binding directly to Models is not acceptable in any but the simplest scenarios; it's also not in the spirit of MVVM.
ViewModels 唯一负责推动更改.这显然包括将更改推送到服务器,但也可能包括将更改推送到应用程序的其他组件.
ViewModels have sole responsibility for pushing changes. This obviously includes pushing changes to the server, but it could also include pushing changes to other components of the application.
为此,ViewModels 可能希望克隆它们包装的模型,以便它们可以在向服务器提供事务时向应用程序的其余部分提供事务语义(即您可以选择何时推送更改应用程序的其余部分,如果每个人都直接绑定到同一个 Model 实例,则您无法执行此操作).隔离这样的更改需要仍然更多的工作,但它也开辟了强大的可能性(例如,撤消更改是微不足道的:只是不要推动它们).
To do this, ViewModels might want to clone the Models they wrap so that they can provide transaction semantics to the rest of the app as they provide to the server (i.e. you can choose when to push changes to the rest of the app, which you cannot do if everyone directly binds to the same Model instance). Isolating changes like this requires still more work, but it also opens up powerful possibilities (e.g. undoing changes is trivial: just don't push them).
ViewModel 依赖于某种数据服务.数据服务是一个应用程序组件,它位于数据存储和使用者之间,并处理它们之间的所有通信.每当 ViewModel 克隆其模型时,它也会订阅适当的数据存储已更改".数据服务公开的事件.
ViewModels have a dependency on some kind of Data Service. The Data Service is an application component that sits between the data store and the consumers and handles all communication between them. Whenever a ViewModel clones its Model it also subscribes to appropriate "data store changed" events that the Data Service exposes.
这允许 ViewModels 收到他们的"更改的通知.其他 ViewModel 已推送到数据存储并做出适当反应的模型.通过适当的抽象,数据存储也可以是任何东西(例如,该特定应用程序中的 WCF 服务).
This allows ViewModels to be notified of changes to "their" model that other ViewModels have pushed to the data store and react appropriately. With proper abstraction, the data store can also be anything at all (e.g. a WCF service in that specific application).
工作流程
创建了一个 ViewModel 并分配了一个模型的所有权.它立即克隆模型并将此克隆公开给视图.依赖于数据服务,它告诉 DS 它想要订阅更新此特定模型的通知.ViewModel 不知道是什么标识了它的 Model(主键"),但它不需要,因为这是 DS 的责任.
A ViewModel is created and assigned ownership of a Model. It immediately clones the Model and exposes this clone to the View. Having a dependency on the Data Service, it tells the DS that it wants to subscribe to notifications for updates this specific Model. The ViewModel does not know what it is that identifies its Model (the "primary key"), but it doesn't need to because that's a responsibility of the DS.
当用户完成编辑时,他们会与调用 VM 上的命令的视图进行交互.然后 VM 调用 DS,推送对其克隆模型所做的更改.
When the user finishes editing they interact with the View which invokes a Command on the VM. The VM then calls into the DS, pushing the changes made to its cloned Model.
DS 保留更改并另外引发一个事件,通知所有其他感兴趣的 VM 已对 Model X 进行更改;新版本的模型作为事件参数的一部分提供.
The DS persists the changes and additionally raises an event that notifies all other interested VMs that changes to Model X have been made; the new version of the Model is supplied as part of the event arguments.
其他已分配相同模型所有权的 VM 现在知道外部更改已经到来.他们现在可以决定如何更新具有手头所有拼图部分的视图(模型的先前"版本,已克隆;脏"版本,即克隆;以及当前"版本).版本,作为事件参数的一部分推送).
Other VMs that have been assigned ownership of the same Model now know that external changes have arrived. They can now decide how to update the View having all pieces of the puzzle at hand (the "previous" version of the Model, which was cloned; the "dirty" version, which is the clone; and the "current" version, which was pushed as part of the event arguments).
注意事项
- 模型的
INotifyPropertyChanged
仅被视图使用;如果 ViewModel 想知道 Model 是否脏",它总是可以将克隆与原始版本进行比较(如果它一直存在,我建议如果可能的话). - ViewModel 以原子方式将更改推送到服务器,这很好,因为它确保数据存储始终处于一致状态.这是一种设计选择,如果您想以不同的方式做事,另一种设计会更合适.
- 服务器可以选择不提出模型已更改";如果 ViewModel 将
this
作为参数传递给推送更改",则负责此更改的 ViewModel 的事件.称呼.即使没有,ViewModel 也可以选择什么都不做,如果它看到当前"模型的版本与其自己的克隆相同. - 通过足够的抽象,可以将更改推送到其他机器上运行的其他进程,就像推送到 shell 中的其他视图一样容易.
- The Model's
INotifyPropertyChanged
is used only by the View; if the ViewModel wants to know whether the Model is "dirty", it can always compare the clone to the original version (if it has been kept around, which I recommend if possible). - The ViewModel pushes changes to the Server atomically, which is good because it ensures that the data store is always in a consistent state. This is a design choice, and if you want to do things differently another design would be more appropriate.
- The Server can opt to not raise the "Model changed" event for the ViewModel that was responsible for this change if the ViewModel passes
this
as a parameter to the "push changes" call. Even if it does not, the ViewModel can choose to do nothing if it sees that the "current" version of the Model is identical to its own clone. - With enough abstraction, changes can be pushed to other processes running on other machines as easily as they can be pushed to other Views in your shell.
Notes
希望这有帮助;如果需要,我可以提供更多说明.
Hope this helps; I can offer more clarification if required.
这篇关于MVVM:绑定到模型,同时保持模型与服务器版本同步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!