保持模型和视图模型同步的最佳实践

6
我正在开发一个大型的Silverlight应用程序,使用双工Net.TCP与WCF后端进行通信。我正在将该应用程序从MVC方法移植到MVVM方法。然而,我正苦恼于实现我的ViewModel的正确方式。我们正在使用WCF生成的代理来处理我们的模型,这是相当复杂的,涉及数十个类、大量集合和各种多对多关系。例如,一个用户可以属于多个房间,一个房间可以有多个用户,一个用户可以拥有多个共享文件,每个共享文件都可以与用户当前所在的任何房间共享。就是这样。
此外,由于我们使用WCF在双工模式下运行,模型的更改可以由最终用户或后端的WCF服务触发。换句话说,我们使用的模型比任何MVVM书籍/文章/博客中展示的典型“模型”复杂得多。这就是问题所在,因为让我们的ViewModel层与底层Model层保持同步已成为一件有点麻烦的事情。
这是一个典型的问题。新用户加入房间后,WCF服务向房间中的所有其他用户发送“SessionAdded”通知。SessionAdded通知携带了一个Session对象,该对象具有链接的Room和链接的User对象。从WCF服务反序列化的Room对象基本上与本地客户端上的Room对象相同,并且可能具有大部分相同的数据,但它肯定不具有完全相同的数据,至少其中一些数据(如其空的Whiteboards集合)肯定是错误的。因此,我们需要以某种方式将这些传入的数据合并到我们现有的模型中。然后,我们需要在每个新对象上创建ViewModels,和/或使用新对象和/或其数据更新现有的ViewModels。

目前,我们通过让各种ViewModel响应相关的WCF通知事件并尽力修复它们的底层模型和相关视图模型来处理这个问题。我们已经想出了一些技巧,比如一个SynchronizedObservableCollection(类似于这里),它监视(例如)Room.Sessions ObservableCollection,并自动创建相应的SessionViewModels并将它们放置在RoomViewModel.SessionViewModels集合中。我们还使用了一个ViewModelFactory,它缓存视图模型并确保,例如,包装给定会话的SessionViewModel保持不变,即使基础会话对象被替换。(如果有关系的话,我们采用视图模型优先的方法,因为我们需要的大部分内容是根据我们的WCF通知触发的ViewModel更改而创建新的UI元素。)

所有这些都有效。基本上。大部分时间。你懂的。但是维护这么多代码很麻烦,而且容易出错。单元测试很方便,只要您记得应该发生什么,但是当您处理第20个级联CollectionChanged事件时,很难跟踪所有这些内容如何配合以及您最初为什么进行测试。换句话说,所有这些都相当脆弱。

我觉得这是许多人必须遇到的情况,我很好奇其他人是如何面对它的。我可以想到几种方法来使其更好:

(1) 将客户端模型视为一种需要完全保持一致的数据库,并实现一个客户端数据访问层,其职责是保持模型的一致性。所有对模型的更新,无论是来自用户还是服务器,都需要通过此层进行。这有点像实体框架,例如myRoom.Users.Add(myUser)将自动设置myUser.Room = myRoom,反之亦然,以此类推。(尤其是这一部分,似乎某个地方应该已经开发出来了,虽然我还没有找到。)

(2) 依赖于类似TrussObtics的东西,以保持所有组件的同步。还不太确定它的工作原理,但从理论上讲,似乎是可行的。

还有什么?我很想知道用于解决此问题的模式或框架。

1个回答

3
我理解您的痛苦 - 我目前正在使用MVVM模式开发一个复杂的数据可视化应用程序。一个非常重要的问题是问自己:“在您使用它的每个地方,View Model是否都增加了价值?”,换句话说,是否有地方仅将其转发到您的视图的模型层上?
我经常发现代码中有些地方,通常在细节层面(例如Person对象的属性,年龄,姓名,名字),视图模型实际上根本没有添加任何价值,而在更粗略的层面上,通过结构化视图/窗口等方式添加了价值...
我倾向于采取适应性的MVVM方法,在顶层(Windows,panes,forms)我总是有一个视图模型,但如果视图模型的某些部分如此简单以至于视图模型不添加任何价值,则直接将其公开给视图。此外,在某些情况下,为了获得更好的性能,需要直接将模型公开给视图。
最后,如果您发现需要重新引入视图模型来解决棘手的绑定问题,我写了一个简单的模式,mini-MVVM,它应用了本地视图模型:

http://www.scottlogic.co.uk/blog/colin/2009/08/the-mini-viewmodel-pattern/

希望这有所帮助。

这基本上就是我在做的事情:我不是一个纯粹主义者 :-)。我的每个ViewModel都有一个Model属性,该属性公开了底层模型。由于此实例中的模型始终是WCF生成的类,因此它实现了INotifyPropertyChanged,因此它作为绑定源运行得相当不错。我在ViewModel中添加的唯一属性是那些实际上以某种方式添加价值的属性。但我仍然必须解决以下问题:当服务器向具有更新模型的客户端发送消息时,需要刷新模型。 - Ken Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接