【问题标题】:MVVM where to put Data Access Layer?MVVM 把数据访问层放在哪里?
【发布时间】:2010-12-15 14:44:35
【问题描述】:

我正在研究 WPF 的 MVVM 设计模式。但不确定将数据访问代码放在哪里?

在我看过的一些示例中,数据访问是直接在 ViewModel 中执行的。在 ViewModel 中放置 linq to sql 之类的东西似乎很奇怪?其他示例有一个单独的数据访问项目,这似乎更像它?

这是一种通用的方法吗?我觉得我在这里错过了一些东西!

谢谢

【问题讨论】:

  • 有时会编写示例代码来说明这一点。应编写生产代码,使其易于维护和理解。

标签: c# design-patterns mvvm


【解决方案1】:

WPF Application Framework (WAF) 包含一个示例应用程序,展示了如何将模型-视图-视图模型 (MVVM) 模式与实体框架结合使用。

【讨论】:

    【解决方案2】:

    以下是我组织 MVVM w/LINQ 项目的方式:

    模型 - 我认为模型是系统的状态。它提供数据接口,并跟踪系统状态。 Model 不知道 ViewModel 或 View——它只是为其数据和各种事件提供一个公共接口,让消费者(通常是 ViewModels)知道状态何时发生变化。

    ViewModel - ViewModel 负责组织或构造视图所需的所有数据,跟踪视图的状态(例如数据网格的当前选定行),并响应视图上的操作(例如按钮按下)。它知道视图需要什么,但它实际上并不知道视图。

    视图 - 视图是 UI 的实际外观。它包含所有内置和自定义控件、它们的排列方式以及样式。它知道 ViewModel,但只是为了绑定到它的属性。

    网关 - 这是直接解决您的问题的部分。网关(基本上是我所说的“DataAccessLayer”)是它自己的独立层。它包含 CRUD 的所有代码(包括 LINQ 查询)或从/向您的数据源(数据库、XML 文件等)选择、插入、更新和删除数据。它还为模型提供了一个公共接口,允许模型专注于维护系统状态,而不必关心更新数据源所需的细节(即查询)。

    DataAccess 类 - 在 C# 中,这些是为基本数据对象建模的非常简单的类。当您使用 LINQ 查询选择某些内容时,您通常会创建一个 IEnumerable<T>List<T>,其中 T 是您的数据对象之一。数据对象的一个​​例子是:

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
    

    这样的设计的最大优势在于它真正将您的关注点分开。每样东西都有一个专门的工作,而且(通常)很容易知道什么样的东西放在哪里。

    缺点是对于小型项目来说可能有点矫枉过正。您最终会为公共接口创建大量基础架构,这些基础架构基本上通过多个层传递一个愿望。所以,你最终可能会遇到这样的场景:[用户点击提交,ViewModel 告诉 Model 到 AddNewPerson,Model 告诉 Gateway 到 InsertPerson] 而不是这样的场景 [用户点击提交,ViewModel 直接向数据库添加新记录]。

    希望对您有所帮助。

    【讨论】:

    • 能否请您提供一个简短的示例来说明您对 mvvm 的解释,因为如果搜索好的 DAL 解决方案,我总是会走到死胡同
    • 在网关中您提到“它还为模型提供公共接口”如果接口由模型决定/控制并由网关实现以实现控制反转,那不是更好在这些层之间?
    【解决方案3】:

    您的 ViewModel 应该是一个仅服务于视图的薄层。我的经验法则:如果它与 UI 的呈现有关,那么它属于 ViewModel,否则它应该在 Model 中。

    【讨论】:

      【解决方案4】:

      数据访问不应在视图模型中,因为这应该是域模型的特定视图(可能是简化的)表示。

      使用某种映射器将您的视图模型(MVVM 中的 VM)映射到您的模型(第一个 M)。可以使用工厂模式创建模型中的新对象。创建后,您可以使用存储库模式将它们存储在数据库中。然后,存储库将代表您的数据访问层。在您的存储库中,您可以使用像 NHibernate 或实体框架这样的 O/R 映射器。

      编辑:
      我看到 GraemeF 建议将数据访问代码放入模型中。这是一个不是的好方法,因为如果您要从例如SQL Server 到 Oracle 或 XML 文件。 域对象不必担心它们是如何持久化的。存储库模式将域与其持久性隔离开来。

      【讨论】:

      • 好点;当然你可以更进一步。我的观点是数据访问代码隐藏在该接口后面,并不存在于 ViewModel 中。
      【解决方案5】:

      MVVM 代表 ModelViewViewModel。您缺少的部分是模型,它是您的数据访问代码所在的位置。

      ViewModel 获取 Model 并将其呈现给 View 进行显示,所以通常你会得到这样的东西:

      class PersonModel : IPerson
      {
          // data access stuff goes in here
          public string Name { get; set; }
      }
      
      class PersonViewModel
      {
          IPerson _person;
      
          public PersonViewModel(IPerson person)
          {
              _person = person;
          }
      
          public Name
          {
              get { return _person.Name; }
              set { _person.Name = value; }
          }
       }
      

      然后PersonView 将绑定到PersonViewModel 的属性,而不是直接绑定到模型本身。在许多情况下,您可能已经有一个对 MVVM 一无所知的数据访问层(也不应该),但您仍然可以构建 ViewModel 以将其呈现给视图。

      【讨论】:

      • 值得注意的是,这在多数据库场景中很难实现,因为您需要将数据库上下文传递给模型。这是我们采用存储库模式的主要原因……将 CRUD 放在单独的类库中。您的里程会有所不同。
      【解决方案6】:

      我会添加另一层,基本上你想要的是一个数据工厂。您想创建一组类,它们将为您对数据库进行 CRUD,并将干净的 POCO 对象返回给 ViewModel。

      Nerd Dinner 这本书是一个很好的例子。它涵盖 MVC 而不是 MVVM,但模式非常相似,并且它们在该解决方案中访问数据的方式将是一个很好的起点。

      希望这会有所帮助。

      【讨论】:

      • 谢谢。我喜欢 Nerd Dinner 使用存储库模式的方式(正如 Kristoffer 所建议的那样)。在文件结构方面,我想我会将 Repository 类和关联的映射器分离到数据访问目录中。似乎它仍然是一个在 MVVM 中基本未提及的大区域。再次感谢。
      • NerdDinner 示例确实是存储库模式的一个很好的示例。然而,它们确实在视图中公开了域模型(即它们不使用 MVVM),这被认为是不好的做法。
      • @Lukasz,在书呆子晚餐中,他们直接调用 linq to sql 方法。可以吗?模型将是由 linqtosql 生成的类?
      猜你喜欢
      • 2016-08-13
      • 1970-01-01
      • 2010-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-14
      • 1970-01-01
      • 2010-12-14
      相关资源
      最近更新 更多