【问题标题】:Is the ViewModel or the View responsible for saving data [closed]是 ViewModel 还是 View 负责保存数据[关闭]
【发布时间】:2021-10-25 03:10:54
【问题描述】:

我不认为这些是重复的:

我正在创建一个本地应用程序。用户输入的数据要么存储在本地文件中,要么存储在本地托管的数据库中。我试图弄清楚我应该如何构造数据的保存。如果相关,我希望仅在用户请求保存时才保存数据(即,我不想在每次更改模型中存储的数据时都写入数据库/文件)。

[旁注建议 V、VM 和 M 都保持同步,是吗?您不想只是偶尔更新模型,对吗?]

按照this articlethis question 中的定义,模型包含业务/应用程序逻辑和数据,而视图模型包含表示逻辑并将模型中的数据转换为可展示的形式。管理(例如储蓄)似乎不太适合这两​​个类别。

问题 1。 如果保存功能(以及在应用程序启动时加载数据)只是在 ViewModel、Model 或其他实体中,则称其为 Controller,因为没有更好的词。

问题 2。 我对 WPF 和 MVVM 都很了解,并且对(/不确定)应用程序结构/架构非常感兴趣。 View 应该如何通知 ViewModel/Model/Controller 用户已请求保存?命令是正确的工具吗(我对命令不熟悉,我刚刚读过一点)。 如果控制器是一个不错的选择,它将具有什么结构,它需要了解哪些 MVVM 组件(或者对所有这些组件都视而不见)。它会在哪里构造(可能在 App.xaml.cs 中?)还是静态对象?

【问题讨论】:

  • 通常我将模型视为具有有限逻辑或根本没有逻辑的简单数据对象,并且用于保存/加载模型对象(或模型上的其他操作)的逻辑确实属于一个或多个服务。模型和服务通常构成领域层(或业务层),但我认为模型一词经常在实际数据模型(或数据传输对象,即 DTO)和领域模型/层之间互换使用。
  • View Models 可以通过依赖注入消费服务来带来服务和数据模型。这些服务通常有一个定义良好的接口,它是传递到视图模型或通过依赖注入容器解析的接口。注入接口对于单元测试和从视图模型中抽象出具体实现很重要。例如,您可能有一个 IModelService,它具有将数据模型保存和加载到文件的逻辑。设置 DI 容器通常在应用程序在 App.xaml.cs 中启动时完成
  • 为服务定义良好的接口也提供了灵活性,因为可以为不同的行为传入不同的具体实现。例如,您可以拥有一个处理将数据加载/保存到文件的服务,另一个处理保存/加载到数据库的实现,也许还有另一个使用 Web API 的服务。关键是视图模型不知道也不关心服务在做什么——它只知道调用 Save()/Load()。 :)
  • @/coding.monkey 听起来我需要了解依赖注入和制作自己的服务。
  • 该模型是 CRUD,但您的数据已公开。可能是某种序列化和反序列化数据的类。您需要从中获取数据并通过它保存数据。这通常涉及某种 DTO。这应该与视图模型分开。 viewmodel 必须实现 inpc 并且可能将数据和命令适应于视图和来自视图。 automapper 通常用于将数据复制到视图模型实例和从视图模型实例复制数据。但这是在您阅读或坚持时完成的。您想要分离关注点,但也因为您只想保留经过验证的数据。

标签: c# wpf mvvm model viewmodel


【解决方案1】:

在我看来,您对不同模式的术语有点困惑。

如果我们考虑 APPLICATIONS(MVC、MVP、MVVM)的 OOP 模式,那么在它们中 View 是 GUI,而 Model 是处理真实数据的层,处理它,包含业务逻辑。
该层是通用的,并且与所使用的 GUI 类型无关,甚至与它的存在与否无关。
从这些模式的角度来看,任何具有真实数据的操作(包括保存数据)都是模型的功能。
但是 Model 本身并不是一个特定的类型,而是一个完整的 Application 层,甚至可以在另一个 Application 内部单独实现(例如,在没有任何 GUI 的情况下在后台运行的服务)。
因此,模型本身可以以许多附加类型的形式实现:具有业务逻辑的核心(在它之下,它们最常表示术语狭义的模型)、存储库、服务等。

在某些模式(ADO、EF 等)中,模型一词也用于表示某些实体,这会引起一些混淆。
假设这通常用于表示反映数据库表中记录的 EF 类型。
从 MVC / MVP / MVVM 模式来看,这些不是模型,而是实体:Database Entity、EF Entity、Business Entity 等。

该模型为消费者提供的不是真实数据(即不是业务实体),而是它们的某种反映(通常这些是不可变的 DTO)。

因此,您的问题的答案是:保存数据是必须仅在模型中实现的功能。 “在自身内部”,模型可以将其委托给它的一些单独的部分,例如存储库。

ViewModel 也是一个模型。但它是为某种类型的 View 设计的,因此必须考虑到这种类型的 View 的特殊性,它的要求。
例如,用于 WPF 的 ViewModel(几乎总是如此),必须在其属性中提供 View 所需的数据,使用 INotifyPropertyChanged、INotifyCollectionChanged、Icommand、IDataErrorInfo 接口和其他要求通知其状态的更改。 在这种情况下,“真实”模型充当 ViewModel 的数据提供者。 因此,ViewModel 不能处理真实数据,而是使用其抽象反射。
ViewModel 无法获得关于真实数据的“知识”(包括存储方式和存储位置)。

由于您需要在 GUI 中为用户提供一种显式保存数据的方法,因此必须将其实现为 ViewModel 中的命令,该命令在其执行方法中调用所需的 Model 方法。
在这种情况下,Model 表示整个对应层。
也就是说,它不一定是业务逻辑模型的主要部分。
例如,它可以是属于模型层的某种服务。

应该理解的是,在实践中经常偏离理想的 MVVM 实现。因此,在实践中,无论是从 View 端还是从 Model 端,您都可以经常在 ViewModel 中看到非特定功能的泄漏。
即使你必须这样做来简化实现,那么你也必须清楚地认识到这是对 MVVM 的一种偏离,虽然你在 ViewModel 中实现了一些特定的功能,但实际上它是 Model(或 View)的一个功能。

关于第二个问题。
MVVM 模式是分层的:顶层是 View,下面是 ViewModel,底层是 Model。
在这样的层次结构中,上层只拥有下层的知识。
即View只对ViewModel“熟悉”,ViewModel只对Model“熟悉”。
一般来说,模型并不“认识”任何人。
应用程序本身(App 类)不是 MVVM 模式的一部分。 它位于所有层之上,因此“了解每个人”。
通常,在应用程序中(可能不在App类中,而是在这一层),初始化层,创建和注入依赖项,其他任务对整个应用程序是通用的。

.Net 中的“通知”指的是事件。
要接收来自某人的通知,您需要订阅此对象的事件。
为此,您需要“熟​​悉”它,即拥有指向它的链接。
但是底层对上层不熟悉。
因此,ViewModel 都不能订阅 View 事件,更不用说 Model 了。

但视图不需要这样的通知。
如果对象想要提供有关其更改的信息,但它不知道谁需要它,则需要通知。
想要接收此类信息的人自己订阅对象的事件。

但视图“知道”它想通知谁 - 这就是 ViewModel。
因此,View 只需调用所需的 ViewModel 方法并将所需的参数传递给它。
这通常在 WPF 中通过绑定到 ViewModel 中的命令属性来完成。

P.S. 文章 "The Model-View-ViewModel Pattern" 提出的观点与我在此处所述的观点非常接近,但更详细。
阅读它可能会对您有所帮助。

引用这篇文章:

Model
模型类是封装应用程序数据的非可视类。因此,模型可以被认为是代表应用程序的领域模型,通常包括数据模型以及业务和验证逻辑。模型对象的示例包括数据传输对象 (DTO)、普通旧 CLR 对象 (POCO) 以及生成的实体和代理对象。
模型类通常与封装数据访问和缓存的服务或存储库结合使用。

【讨论】:

  • 我发现您关于真实数据和非真实数据的术语非常令人困惑。 View Model 只是将数据呈现给 View。但不负责控制持久性。它不知道数据从哪里读取和写入到哪里。但它当然处理真实数据。您似乎将数据与数据实体混淆了。
  • 也将 MVVM 描述为分层(以视图为根)是不正确的,并且会产生错误的关联。当 MVVM 描述应用程序的组件时,模型是基础——而不是视图。模型定义了应用程序——它将是金字塔的基础或树的根。您应该将 MVVM 视为描述构建应用程序的组件的职责和单向关系的模式,而不是分层树结构。
  • 同样在架构方面 MVVM 没有描述分层架构。分层架构与 MVVM 应用程序组件结构并存。 MVVM 的存在旨在将视图与业务逻辑分离。您可以根据业务层和持久层等层架构设计模型组件。从架构的角度来看,模型不是一个层,而是一个可以使用分层架构模式设计的应用程序级组件。
  • @BionicCode,很抱歉,我完全没有追求向你解释一些事情的目的。我认为您无需我的解释就可以很好地理解所有内容。据我所知,我的解释是针对 TS 的。要完整解释所有细微差别,您需要编写厚厚的教科书。我没有这样的目标。对我来说,此时此地回答更重要,这样 TC 此刻就可以从这个答案中受益于他的实践。
  • @Commonaught,我用一个简单的 MVVM 文章的链接补充了我的答案。阅读。我认为这对你有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
相关资源
最近更新 更多