我正在使用大型Silverlight应用程序,该应用程序使用双工Net.TCP与WCF后端通信。我正在将这个应用程序从MVC方法迁移到MVVM。但是,我正在努力实现我的ViewModels的正确方法。我们将WCF生成的代理用于我们的模型,该模型非常复杂,涉及数十个类,大量集合以及各种多对多关系。例如,一个用户可以属于多个房间,一个房间可以具有多个用户,一个用户可以具有多个SharedFiles,并且每个SharedFile可以与该用户当前所属的任何Room共享。这样的事情。
最重要的是,因为我们在双工模式下使用WCF,所以可以由最终用户或后端的WCF服务触发对模型的更改。换句话说,我们所使用的模型比您在各种MVVM书籍/文章/博客文章中看到的典型“模型”要复杂几个数量级。这就是问题所在,因为使我们的ViewModel层与基础Model层保持同步变得有点麻烦。
这是一个典型的问题。新的“用户”加入“房间”,因此WCF服务向房间中的所有其他用户触发“ SessionAdded”通知。 SessionAdded通知带有一个Session对象,该Session对象具有一个链接的Room和一个链接的User对象。从WCF服务反序列化的这个Room对象与本地客户端上的Room对象基本相同,并且可能具有大多数相同的数据,但是肯定没有all相同的数据,并且至少某些数据(例如其空白的Whiteboards集合)肯定是错误的。因此,我们需要以某种方式获取这些传入数据并将其合并到我们现有的模型中。然后,我们需要在每个新对象的顶部创建ViewModel,和/或使用新对象和/或其数据更新现有的ViewModel。
目前,我们通过让各种ViewModel响应相关的WCF通知事件来处理此问题,并尽力修复其基础模型和相关的View模型。我们已经找到了一些技巧,例如SynchronizedObservableCollection(类似于here),它监视(说)Room.Sessions ObservableCollection并自动创建相应的SessionViewModels并将其放置在RoomViewModel.SessionViewModels集合中。我们还使用了ViewModelFactory,它缓存视图模型,并确保包装了给定Session的SessionViewModel保持不变,即使基础Session对象被更改。 (如果重要的话,我们使用的是viewmodel-first方法,因为我们需要的大部分工作是创建新的UI元素,以响应由WCF通知触发的ViewModel的更改。)
所有这些都有效。基本上。大多数时候。你懂。但是要维护的代码很多,而且很容易出错。只要您能记住应该发生的情况,单元测试就很方便,但是到完成第20个级联CollectionChanged事件的处理时,很难跟踪所有这些如何组合以及首先要进行的测试。换句话说,它们都非常脆弱。
在我看来,这是很多人必须遇到的那种情况,我很好奇其他人如何面对它。我可以想到一些方法来使它变得更好:
((1)将客户端模型视为需要完全保持一致的一种数据库,并实现一个客户端数据访问层,其任务是保持模型一致。无论是从用户还是从服务器,对模型的所有更新都需要经过这一层。这有点像实体框架,因为myRoom.Users.Add(myUser)
将自动设置myUser.Room = myRoom
,反之亦然,依此类推。 (特别是这部分内容似乎应该已经在某个地方发展了,尽管我还没有找到。)
((2)依靠Truss或Obtics之类的东西,使所有片段保持同步。尚不确定该如何工作,但理论上听起来应该可行。
还有...还有什么?我对用于解决此问题的模式或框架感到好奇。
我了解您的痛苦-我目前正在使用MVVM模式开发复杂的数据可视化应用程序。要问自己一个非常重要的问题是:“视图模型是否会在您正在使用的所有地方增加价值?”换句话说,是否有地方只是将模型层的属性转发给视图?
我经常发现,通常在细节级别(例如,Person对象的属性,Age,Name,Forename)的代码区域中,视图模型实际上根本没有添加任何值,而在更多方面,级别,它通过构造视图/窗口等来增加价值...
我倾向于对MVVM采取自适应方法,在顶层(Windows,窗格,表单),我总是有一个视图模型,但是如果视图模型的某些部分是如此简单,以至于视图模型没有任何价值,那么我会公开他们直接到视图。另外,在某些情况下,您需要直接将模型公开给视图以提高性能。
最后,如果您发现需要重新引入视图模型来解决棘手的绑定问题,我写了一个简单的模式mini-MVVM,它应用了本地视图模型:
http://www.scottlogic.co.uk/blog/colin/2009/08/the-mini-viewmodel-pattern/
希望有所帮助。