将数据传递到数据层

4
我的模型由一个主对象组成,用户可以向其中添加各种其他对象。添加的对象存储在主对象及其关联子对象中包含的 List<object> 中。
因此,如果项目是一座房屋,则用户可以向房屋添加多个 Room 对象,这些对象存储在 List<Room> RoomList 中。然后,每个 Room 可以添加多个 Furnishings,同样存储在每个 Room 的 List<Furnishing> FurnishingsList 中。
问题是如何在 MVVM 方法中实现这一点?当用户添加新对象时,我是否应该将其添加到 ViewModel 中的 ObservableCollection 中,以便更新视图,并同时将其添加到连接到 VM 的模型中?还是将其存储在 VM 中,直到保存或提交命令,然后将其传递给模型?
在我的示例中,我有各种编辑器(每个都是用户控件)。因此,用户可以使用一个编辑器高级别地编辑房屋,添加、编辑和删除房间。在较低级别上,使用不同的编辑器编辑每个房间,添加和删除 Furnishings
因此,当用户“编辑”一个房间时。我会生成一个包含该 RoomEditRoomModelView。用户可以在其中添加、编辑和操纵该房间的家具。
在每个命令中,最好像上面那样同步模型和 ModelView 中的数据。还是将更改直接推送到模型中,并且 ViewModel 仅在注入的模型属性上提供 getter。但是,这种方式将对象添加到模型列表中不会更新视图。我真的必须同时将数据添加到 ModelView 和 Model 中,以便一切处于相同状态。
对于冗长的陈述,很抱歉,我很难找到一个好的方法来解决这个问题,请问是否有人理解我的意思?
3个回答

3
我不确定是否有一个单一的,好的答案。看起来你正在考虑如何保持一致性——你是从演示层中简单地传递方案,还是在用户请求上做些修改和逻辑处理。
如果确实是这样的问题,那么就有各种因素需要考虑。你是否喜欢在演示层中进行验证?应用程序有多复杂,需要多可扩展?您的视图模型是否需要在多个地方访问?
所以,考虑到所有这些因素,我可以告诉你我个人处理这个问题(具体来说,是你提到的添加问题)的偏好。我通常喜欢创建描述用户关注点的视图模型,使用通用的泛型,比如 `EditItemViewModel` 或者 `ListViewModel`。所以,在 GUI 中,会有某种绑定到 `ListViewModel` 的房间列表框。这个 VM 显然会公开一个可观察集合,还有添加、删除、编辑等命令。
因此,从我的角度来看,演示层和视图模型都会处理对其他 GUI 关注点的请求。如果你点击“添加”以添加一个房间,这个 VM 负责通过命令发起请求,获取房间添加所需的任何屏幕/窗口/其他内容,它将拥有自己的 VM。该 VM 在收到添加请求后,将传递生成的数据传输对象到域中,进行验证和必要的域操作。我通常通过服务层处理这个问题。现在,如果域操作成功,服务层将引发某种事件或回调,让列表 VM 知道有所改变。当发生这种情况时,列表 VM 重新查询其服务并相应地更新其可观察集合。现在,您的 GUI 与整个域一致。
我偏爱这种分层方法的原因是所有的业务逻辑都发生在 GUI“下面”的地方,GUI 不需要关心这种情况。从概念上讲,GUI 只是说:“嗨,域层,用户想添加这个——做所有的事情,并让任何感兴趣的 GUI 组件知道你什么时候完成了,以便它们可以刷新自己。”
我在这里描述的内容自然会比一个简单的传递方案或者 VM 仅仅重新暴露模型对象的属性的方案多出一些开销。但是,就我个人而言,从解耦的角度来看,这种优势是值得的,尤其是在扩展应用程序时。它使您能够基本上更改域模型的内部交互,而不改变演示层代码。

谢谢Erik,当我成功将新对象传递到领域层并存储在List<object>中时,如何刷新VM的ObservableCollection以表示List<object>?服务层是否重新实例化ObservableCollection以反映更改? - Cadair Idris
我不会让演示层以下的任何东西知道ObservableCollection是什么(即没有引用包含它的WPF程序集)。我通常使用事件或回调来通知GUI组件。举个简单的例子,考虑两个具有对同一服务实例的引用的VM。VM A告诉服务添加一个房间,服务执行必要的操作以添加房间。当完成并成功时,它会引发一个事件,VM B监听该事件。当事件被触发时,VM B重新查询所有房间的服务... - Erik Dietrich
在实际实现中,领域本身将处理添加操作并引发一个事件,服务将消费该事件并传递给虚拟机。但这里的想法是这样的 - 虚拟机只知道如何向服务请求对象(POCO/DTO),以及如何将它们传递给更新等操作。因此,服务和虚拟机之间的关系是,虚拟机将请求对象(Get/GetAll)和请求操作(Add、Edit等)。虚拟机还会监听一个名为"OnDirty"的服务事件,告诉虚拟机何时可能与领域不同步。然后,虚拟机有重新查询的负担。 - Erik Dietrich

2

首先:View是您的DataModel的镜像,因此始终首要添加到Model中,然后再考虑是否将其推送到VmView上。

其次:ViewModel是连接ModelView的桥梁,但并不意味着它必须按照Model的方式进行构造。如果您想向用户显示所有子集合,则必须像属性一样公开它们,并在VM内部构建它们的父子关系,或者在VM中仅具有“原始”集合,并在DataModel中有效地对它们之间的关系进行处理。


我不同意“总是先将内容添加到模型中,然后再考虑是否将其推送到 Vm 或 View”的说法,视图模型创建其他视图模型实例没有任何问题,例如 OrderViewModel 创建新的 OrderLineViewModel 实例。 - ColinE
如果用户添加了一个新对象,我认为不应该在 UI 层结束。等待什么?这不是 Web 开发,在那里我们有时会在 UI 层保留数据以进行验证、聚合等操作,因为数据传输很昂贵,但在桌面应用程序中,我看不到任何理由为什么在我的应用程序生命周期的任何时刻我都不能询问模型有关当前状态的信息并得到有效的响应。 - Tigran

2
从您所描述的应用程序来看,似乎您的视图模型层不会对模型进行重大修改或塑造。因此,我建议将各种视图模型属性的setter/getter设置为包装模型对象的简单适配器。这样,当视图模型更改时,您的模型将立即更新。
然而...如果您需要提供“取消”功能,即用户打开模态表单以编辑某个视图模型,但取消而不是确认编辑,则这种简单方法将无法工作。
通常情况下,提交到模型层的时间很少有太大的区别,因为这通常不是一个耗时的过程。更重要的是模型状态何时持久化。这通常是通过显式调用保存命令触发的,该命令将模型发送到持久化服务(文件/云等...)

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