3

我有一个 asp.net-mvc 站点,它几乎是一个 CRUD 应用程序,但我也在 web 视图之外进行了一些添加和更新(从电子表格等上传)。 基于这篇文章,我试图将 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 项目之外,以便可以重用这些对象。

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

4

8 回答 8

6

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

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

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

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

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

_orderService.UpdateOrder(order.Id, order.Name, order.Price);
于 2013-08-04T21:31:33.460 回答
3

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

读取模型非常特定于视图,它们可以包含选择列表以及格式化和本地化的内容。您不应将此模型移出您的 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 添加到您的视图模型中。write-model 包含所有数据,这些数据将通过 POST 发送回您的服务器,然后直接发送到您的域层。
于 2013-08-09T11:42:52.620 回答
1

我在两个组件中使用了四个“层”

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

1) {application}.model 用于域模型

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

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

{application}.WebUI UI 程序集,这是 MVC 项目

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

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

OrderServices.Update(现有订单)

OrderServices.Approve(现有订单)

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

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

希望这可以帮助。

此致。

于 2013-08-11T04:04:24.957 回答
0

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

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

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

我会回答的问题。

希望能帮助到你。

于 2013-08-13T20:06:21.100 回答
0

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

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

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

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

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

希望能帮助到你...

于 2013-08-13T15:38:21.880 回答
0

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

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

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

于 2013-08-12T10:26:19.903 回答
0

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

 IOrderService<Order> service = new OrderEntityService();

并像使用它一样

service.Create(Order) or service.Update();
于 2013-08-04T16:08:53.423 回答
-1

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

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

var customerInfo = Mapper.Map<CustomerViewModel, CustomerInfo>(customerViewModel);
于 2013-08-04T17:25:32.197 回答