【问题标题】:In asp.net-mvc, Is it wrong to move my "EditViewModel" classes outside my MVC web project?在 asp.net-mvc 中,将我的“EditViewModel”类移到我的 MVC Web 项目之外是错误的吗?
【发布时间】:2013-08-04 11:29:42
【问题描述】:

我有一个 asp.net-mvc 站点,它几乎是一个 CRUD 应用程序,但我也在 web 视图之外进行了一些添加和更新(从电子表格上传等)。 Based on this article,我正在尝试将 MVC 项目之外的尽可能多的逻辑放入一个单独的共享项目中,以便我可以在所有场景中重用相同的代码,并且我正在尝试隔离和分离绑定到的“读取”视图模型用于从“编辑”视图模型中显示的 UI,这些视图模型表示在表单发布中发布到服务器的内容。

我在解决方案中有许多项目(域对象、importexport 等)与其他解决方案共享,而且 MVC 项目在 MVC 项目中有以下目录

  • 控制器
  • 观看次数
  • 视图模型
  • 脚本
  • 编辑视图模型

我的 ViewModels 文件夹代表我绑定到我的视图的对象(一个容器对象,通常包括:

  • 域对象和
  • 一堆 IEnumerable 的 SelectListItem 来填充 UI 下拉列表等

类似这样的:

 public class OrderViewModel
 {
       public Order MyOrder {get;set;}
       public IEnumerable<SelectListItem> OrderTypes {get;set;}
       public IEnumerable<SelectListItem> Sizes {get;set;}
 }

我的 EditViewModels 文件夹代表我从表单发布到服务器的对象,因此它们通常是更简单的平面对象,只有基元,然后我在添加或更新我的数据库之前填充域对象:

public class OrderEditViewModel
{
      public int Id {get;set;}
      public int OrderTypeId {get;set;}
      public int SizeId {get;set;}
}

我的主要问题是当我的 Controller 类中有一个通常看起来像这样(简化)的方法时:

  public ActionResult Update(OrderEditViewModel order)
  {
         var existingOrder = _repository.GetOrder(order.Id);
         existingOrder.Name = order.Name;
         existingOrder.Size = _repository.GetSize(order.SizeId);
         existingOrder.Price = order.Price;
         _repository.Save(existingOrder);
         return Json( Result = "Success");
  }

我试图弄清楚如何在 MVC 项目之外获取尽可能多的代码,但这迫使我将 EditViewModel 中的所有类移到 MVC 项目之外,以便可以重用这些对象。

有没有人认为将这些“Post”数据结构归类在 MVC 项目之外有什么问题。鉴于它是一个“视图模型”类,将其从视图中移开感觉不对,但我看不到任何其他方式可以在 MVC 项目之外共享此代码。也许这里的“viewModel”名称可能是错误的?

【问题讨论】:

    标签: asp.net-mvc viewmodel


    【解决方案1】:

    您的视图模型特定于您的视图,不应与其他任何内容相关。它们包含其他任何不应该关心的东西,例如您的选择列表。因此,它们应该保留在您的 UI 中。

    在我看来,这篇文章是在创造一种业务逻辑依赖于 UI(或至少是 UI 模型。即视图模型)的情况,我认为这是错误的方法。 UI 应该能够在业务逻辑不知道 UI 是什么的情况下使用业务逻辑。它可以是一个网站、一个胖客户端、一个移动客户端、一个 Web 服务等...通过使这个逻辑依赖于您的视图模型,您现在可以让任何未来的非基于 Web 的服务都依赖于这些。

    然而,这是一个简单的 CRUD 应用程序,在简单的 CRUD 应用程序中,您通常可以走很多捷径,因为这不值得额外的工程工作。我通常不会直接在视图模型中将域对象传递给视图。但在这种情况下,它可能是最好的选择。

    但是,如果您想“正确”地做到这一点,那么您需要创建更多的关注点分离。您的域和 ui 层需要更多的分离。您创建单独的视图和域模型,然后在它们之间进行映射。这会阻止业务层了解有关 UI 的任何信息。

    我可能会创建一个服务层来处理您的逻辑。例如:

    _orderService.UpdateOrder(order.Id, order.Name, order.Price);
    

    【讨论】:

    • 我倾向于同意你的观点。 .
    【解决方案2】:

    就我而言,我在读(视图)模型和写模型之间有所不同。

    读取模型非常特定于视图,它们可以包含选择列表以及格式化和本地化的内容。您不应将此模型移出您的 ui 项目。当然,您可以使用您的模型制作一个单独的组件,或者您可以为每个模块制作一个组件,但您永远不应该从您的领域层使用这些模型。

    在我看来,编写模型并不特定于您的 UI。相反,它们代表命令所需的数据(例如 SaveUserCommand)。它们可以包含验证属性,因此您的域层可以轻松地验证它们,并且它们可以由域层和 UI 共享。在我的项目中,每个命令(例如 SaveUserCommand、EditUserCommand、DeleteUserCommand)和相关模型(SaveUserModel、EditUserModel)都有一个类。有人会评论说,它们还包含一些特定于 UI 的代码(例如,用于属性的 IClientValidateable itnerface 或属性本身),并且至少 IClientValidateable 接口是一个我愿意忽略以减少模型数量的问题。我对这种方法的经验(我也尝试过其他方法)表明,这些模型非常简单,并且很容易绑定到这些模型。

    有时您也会遇到问题,即您想在编辑视图中显示一些附加信息。我不会将所有这些信息都添加到 viewbag 中,而是使用另一个模型,例如:

     class UserEditModel 
     {
         string Password;
     }
    
     UserEditViewModel
     {
         DateTime Modified;
    
         UserEditModel Edit;
     }
    

    所以我的建议:

    1. 创建包含特定于您的用例的所有数据和验证逻辑的写入模型。
    2. 创建包含您要显示的所有数据的视图模型,并尽量不要使用 ViewBag。
    3. 如果您有一个表单,请将 write-model 添加到您的视图模型中。写入模型包含所有数据,这些数据将通过 POST 发送回您的服务器,然后直接发送到您的域层。

    【讨论】:

      【解决方案3】:

      我在两个程序集中使用了四个“层”

      {application}.app 具有三个命名空间的程序集,只是一个普通的类库项目。

      1) 域模型的 {application}.model

      2) {application}.data 用于使用存储库模式的数据

      3) {application}.services 用于所有业务逻辑

      {application}.WebUI UI 组件,这是 MVC 项目

      控制器只调用服务,服务通过存储库获取和更新数据。

      您的应用必须执行的每项操作都有一个服务 API,即

      OrderServices.Update(现有订单)

      OrderServices.Approve(现有订单)

      服务层只知道领域模型,控制器只是组装视图模型,使用从服务中获取的领域模型,将它们发送到视图,并使用从视图中获取的视图模型准备域模型,然后将它们发送到适当的服务.

      通过这种方式,您最终可以创建一个 {application}.WebAPI 或使用相同 {application}.app 程序集的任何东西,重用所有业务逻辑并将 viwemodel 保留在它们所属的位置(WebUI 项目)

      希望这会有所帮助。

      最好的问候。

      【讨论】:

      • 请注意!这几乎就是我的做法,只是我的 .Data、.Domain 和 .Services 层都在它们自己的项目/DLL 中。
      • 现在我决定根据解决方案的大小拆分项目,如果它“小”,我将它保留在一个项目中。我正在尝试建立一个独立于我是否决定拆分的名称空间结构。您可能想看看:github.com/mvelosop/MicroFlow
      【解决方案4】:

      您可以使用项目的服务架构,其中所有函数和数据库查询都在此文件中,您只需添加此代码即可使用

       IOrderService<Order> service = new OrderEntityService();
      

      并像使用它一样使用它

      service.Create(Order) or service.Update();
      

      【讨论】:

        【解决方案5】:

        让我感到奇怪的是,您尽可能多地对应用进行分层处理,但您仍然让 MVC 控制器包含大量逻辑。您正在尝试共享对象和模型,但添加新 OrderEditViewModel 的逻辑未共享,它卡在该控制器中。

        我尝试为我的每个控制器创建一个“助手”或“逻辑”类。然后将该逻辑或辅助类注入我的控制器并包装在一个接口中。我的控制器可以通过我的助手类保存、编辑和删除项目,但不知道它实际上是如何做到的。

        然后我将这些逻辑类连同模型共享给其他项目,允许大量代码重用。问题只是确保控制器的任何“HTTP'ness”都不会潜入您的逻辑类,因为这些必须在控制台或 winforms 应用程序中可用。所以你必须非常严格,将很多东西,比如 HTTPSession 或 HTTPContext 包装到接口中,然后可以有非 HTTP 实现..

        【讨论】:

          【解决方案6】:

          嗯,我了解您的情况,并且也倾向于采用 @MystereMan 建议的 DDD(域驱动设计)解决方案。

          我有一种方法可以将“模型”分为 3 个类别:

          1. ViewModel:拥有在 UI 中显示内容数据所需的所有信息
          2. RequestModels:拥有发送数据、发布/获取/等所需的所有信息。)
          3. AutoBindModels:拥有注入我的 MVC 绑定模型的所有信息(cookie、会话等)

          最重要的是,对于我用作 DTO/POCO 的所有类,实际上它们没有依赖于 UI 的代码,只有属性/计算的属性,并且可以很容易地在任何其他项目中被 UI 项目引用。

          您还可以在 ASP.MVC 项目之外创建一个充当服务的 Controller 类,并将其扩展或注入到您的 MVC 控制器中。

          希望对你有帮助...

          【讨论】:

            【解决方案7】:

            我从不重用 (Edit)ViewModels 对象(这并不完全正确,我经常在 Create 和 Update 之间共享一个 EditViewModel,但并非总是如此)。

            我为特定视图设计我的 ViewModel。因此,当 UI 发生变化(它一直在变化)时,我不必妥协我的 ViewModel。

            我认为这是创建两个不同的 ViewModel,即使它们是相同的。我已经完成了对共享 ViewModel 的重构..

            我会回答你的问题。

            希望对你有帮助。

            【讨论】:

              【解决方案8】:

              OrderEditViewModelOrderViewModel 都是一天结束的“ViewModels”。 IMO,即使在您的同一个“ViewModels”文件夹中,它们也可能留在同一个项目中。您可以在 ViewModels 下为您的“EditViewModels”创建一个子文件夹。

              现在,当您想要清理/整理控制器操作时,您可能需要使用 AutoMapperValueInjecter。您正在手动映射域实体和视图模型。这是一项令人厌烦的工作。使用 AutoMapper,您可以执行以下操作:

              var customerInfo = Mapper.Map<CustomerViewModel, CustomerInfo>(customerViewModel);
              

              【讨论】:

              • 这根本没有谈到我的要求,那么你的更新逻辑应该放在哪里?
              • @leora “我正试图弄清楚如何在 MVC 项目之外获取尽可能多的代码。” 这可能是我的看法。不管怎样,谢谢你鼓励我。
              • 明白,但问题的核心部分是这些 EditViewModel 类应该转移到应用程序的业务端还是保留在 UI 中,如果是,数据更新逻辑到哪里去
              猜你喜欢
              • 2020-08-03
              • 2021-09-09
              • 2012-01-28
              • 2010-09-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-01-04
              相关资源
              最近更新 更多