【问题标题】:Always use ViewModel pattern in MVC? [closed]总是在 MVC 中使用 ViewModel 模式? [关闭]
【发布时间】:2014-05-18 14:55:52
【问题描述】:

在控制器的 MVC 中,您应该从 DB 中获取模型并将其转换为 ViewModel,然后再将其发送到视图。通常使用 Automapper 之类的东西。

我的问题是,如果您需要在视图中显示模型的所有属性,那么创建一个 ViewModel 是否值得?

如果 Model 和 ViewModel 需要相同,那么创建 ViewModel 会为应用带来一些安全性或好处,或者我们只是增加了不必要的复杂性?

【问题讨论】:

  • 如果您希望网站随着时间的推移而更新,您绝对应该使用视图模型
  • 这个问题属于 Programmers.SE

标签: c# asp.net-mvc asp.net-mvc-5 asp.net-mvc-viewmodel


【解决方案1】:

使用视图模型的目的通常是因为您的视图需要的信息比您的域模型提供的信息更多/更少。

其他好处包括将您的视图与您的域分离,如果您的域发展,这可能会导致脆弱性。

关键是,它们不是必需品;视图模型的目的是为视图提供它需要呈现自身的信息,如果您觉得应用程序中的视图模型是多余的,请不要使用它。不过,我至少会考虑使用接口来避免耦合。

【讨论】:

    【解决方案2】:

    除了已经提到的(关注点分离、解耦等)之外,单独的 ViewModel 可以阻止可能与 DB 模型一起出现的抽象泄漏。如果您使用启用了导航属性的 EF,则尤其如此。

    假设您有汽车和车轮。您正在视图中显示汽车。

    案例 1(汽车没有单独的 ViewModel): 在 razor 视图中,很容易出现如下内容:

      public class CarModelFromDB
      {
          public string CarName{get;set;}
          //More properties
          public IEnumerable<Wheel> Wheel{get;set;}
      }  
    
      @model IEnumerable<CarModelFromDB>
    
      @foreach(var car in Model)
      {
          //some View Code
          @foreach(var wheel in car.Wheels.Where(x=>x.IsRound=true))
          {
             <span>wheel.Color</span> 
             // some View Code
          }
      }
    

    现在,您获取汽车轮子的逻辑已泄露到视图中,并且还启用了select N+1 situation。我也不认为有任何简单的测试方法。

    案例 2(使用 ViewModel 用于汽车):在这种情况下,您可以通过仅发送它需要的内容来限制视图。它可能如下所示:

      public class CarViewModel
      {
          public string CarName{get;set;}
          //More properties
          public IEnumerable<string> WheelColors{get;set;}
      }  
    
      @model IEnumerable<CarViewModel>
      @foreach(var car in Model)
      {
          //some View Code
          @foreach(var wheelColor in WheelColor)
          {
             <span>wheelColor</span> 
             // some View Code
          }
      }
    

    现在,您的视图代码的功能非常有限,它不会向数据库发送任何恶意查询。您的控制器真正控制了视图的获取方式。您可以将轮逻辑推到那里,或者理想情况下推到从操作方法调用的某些服务方法中。此外,您可以对操作方法进行适当的测试,并对您的系统充满信心。我希望这会有所帮助。

    更新

    案例 3(动态 ViewModel): 如果您对dynamic types 感到满意,则可以避免所有的强制转换和映射。只要您的视图获得所需的属性,它就会很高兴。这些从哪里来并不重要。所以代码将是:

      public class CarViewModel
      {
          public string CarName{get;set;}
          //More properties
          public IEnumerable<string> WheelColors{get;set;}
      }  
    
      // pass the List<CarViewModel> to the view
    
      @model dynamic
      @foreach(var car in Model)
      {
          //some View Code
          @foreach(var wheelColor in WheelColor)
          {
             <span>wheelColor</span> 
             // some View Code
          }
      }
    

    潜在的缺点/额外工作是确保您对模型上这些属性的存在进行了测试。

    再次像前面提到的那样,这不应被视为一种适合所有类型的解决方案。这些是一些选项,仅在有意义时才使用。

    【讨论】:

      【解决方案3】:

      虽然这个问题很可能会因为主要基于意见而被关闭,但这确实是一个好问题。 ViewModel 几乎总是值得在边缘复杂的应用程序中付出努力。

      大多数应用都将从BaseViewModel.cs 扩展所有Viewmodels 受益,因此CurrentTab(突出显示当前导航栏页面)或PageTitle 等内容可以显示在_Layout.cshtml 中。在这种情况下,视图模型始终是BaseViewModel 的派生项。

      【讨论】:

      • 我非常不同意基本视图模型。这些东西往往会成为您可能想要在其他地方重复使用的每个设置的垃圾场,并且在您知道它之前,它有大量不必要的垃圾。如果您需要这样的常见行为,我倾向于将其封装在它自己的类中,或者将其包含在您的模型中(如有必要),或者通过将该代码提取到它自己的操作中来使其完全没有必要。继承是一种“is-a”关系,而不仅仅是共享数据的便捷方式。
      • @Erik BaseViewModel 的主要目的是消除对ViewBagViewData 的需求,这在强类型语言中是糟糕的设计选择。而某个Type的每个类都需要的通用核心数据,例如页面标题或者当前标​​签页是什么,正是实现Base Class的目的。
      • 我不同意,并且已经给出了我的理由。我的解决方案不使用 ViewBag 或 ViewData,而是以更易于维护的方式执行完全相同的操作,而不会滥用继承作为共享数据的一种方式。有时基类是必要的,但人们往往很懒惰,这是他们跳到的第一件事,而不是使用更好的解耦解决方案。我不同意这种说法,即“大多数应用程序都将从中受益”,这显然是一个糟糕的设计选择。
      • @Erik 如果你决定每个ViewModel,无论它显示什么,都需要被告知它是当前标签,一个“is-a”关系.继承是拥有父类属性的正确选择,每个派生对象都需要这些属性才能成为完整的ViewModel。这显然是正确的设计选择,我鼓励您客观地重新阅读使某事物成为 Parent ClassUtility Class 的原因。
      • 我不同意。我在哪个选项卡上与视图模型无关。也就是页面的功能。您的导航系统应呈现为具有自己模型的子操作。您混合了职责并违反了单一职责原则。基类只能作为最后的手段使用,还有许多其他选项可以做到这一点,不需要用基类污染你的模型。
      【解决方案4】:

      这是不可能的,因为我们不知道你的模型是什么以及是否存在任何安全问题。

      一般来说,安全问题不在于将模型传递给视图,而在于接收自动绑定到数据模型的帖子。如果您在其中包含敏感信息,则有人可能会制作一个帖子来改变您不希望发生的事情。

      http://odetocode.com/blogs/scott/archive/2012/03/12/complete-guide-to-mass-assignment-in-asp-net-mvc.aspx

      更大的问题是,它们真的一样吗?你确定它们会保持不变吗?您确定不需要进行需要视图模型与数据模型有不同要求的更改吗?

      提前做好工作并遵循良好的设计,比在需要时尝试修复它更容易。

      【讨论】:

        【解决方案5】:

        您的假设是正确的,即额外的层会增加复杂性、冗余、额外的映射和同步开发时间。但是这个缺点很容易被大型复杂应用程序所吸收,特别是在不同团队被分配到不同层的团队环境中。

        对于有几个开发人员(在您的情况下通常是 1 个)的小型项目,可以在您的 UI 中使用相同的模型。体积小,当它长大时,你可以很容易地拍打额外的层。

        ViewModel的重点是支持关注点分离,这是MVC的主要卖点。这种分离允许您的演示文稿和域安全地独立发展。您可以保护域免受演示文稿中的任何更改,并且您可以保护演示文稿免受域中的任何更改。

        您可能不会在早期注意到这一点,但从长远来看,域和表示肯定会发生变化,并且您的代码需要在发生这种情况时准备好。你今天的计划会让你明天保持清醒。

        此外,还有很多方法可以自动从域模型映射到视图模型,例如 AutoMapper 等工具。

        【讨论】:

          猜你喜欢
          • 2023-04-07
          • 2010-11-16
          • 1970-01-01
          • 2011-08-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-06
          • 2013-01-20
          • 2013-08-06
          相关资源
          最近更新 更多