你是如何填充/验证你的 ViewModel 的?

Posted

技术标签:

【中文标题】你是如何填充/验证你的 ViewModel 的?【英文标题】:How are you populating/validating your ViewModels? 【发布时间】:2011-12-18 02:05:07 【问题描述】:

我很好奇人们构建 ViewModel 的各种方式以及他们选择这种方法的原因。

我可以在这里想到几种方法:

-1。注入存储库 - 控制器加载模型并映射到 ViewModel。在这里,ViewModel 构造函数可以采用各种集合来为 ex 进行交互设置。在选择列表中,例如:

public CustomerController(ISomeRepository repository) _repository = repository; public ActionResult Create() CustomerCreateViewModel model = new CustomerCreateViewModel(_repository.GetShipTypes, _repository.GetStates); .. ..

-2。 ViewModelBuilder - 在控制器中注入或实例化注入存储库的实例。通过类似

的方式调用
>var orderViewModel = orderViewModelBuilder.WithStates().Build(orderId);

或者,

var orderViewModel = orderViewModelBuilder.WithStates().Build(orderId);

-3。直接在控制器中(不需要代码 - 它很乱)

-4。其他一些服务(注入与否)返回控制器然后映射的域模型或 ViewModel(任何人这样做是为了返回一个没有特别命名/标注为 ViewModel 构建器类的视图模型?)


public JobCreateViewModel BuildJobCreateViewModel(int parentId)

   JobCreateViewModel model = new JobCreateViewModel();
   model.JobStatus = _unitOfWork.JobRepository.GetJobStatuses();
   model.States=_unitOfWork.StateRepository.GetAll();
   return model;

现在在回程中 - 关于验证您的视图模型 - 您是从基本 ViewModel 类继承以进行标准验证,还是在所有 ViewModel 之间复制您的验证(例如数据注释属性),或者只是依赖服务器端验证,以便可以针对您的域对象进行验证?

还有其他人吗?有更好的吗?为什么?

编辑 根据下面的链接,我确实找到了 Jimmy Bogard 的一篇关于 ViewModels 架构的好文章。虽然它没有直接解决上述问题,但对于任何来这里获取 ViewModel 信息的人来说,它都是一个很好的参考。 http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/

【问题讨论】:

这里有人反对 - 只是好奇为什么 - 有什么我可以澄清的吗? 在我的手机上,我在尝试加注星标时无意中点击了它——抱歉。现在撤消为时已晚。 【参考方案1】:

我将服务注入控制器,而不是存储库,然后使用AutoMapper 将其转换为视图模型。在这种情况下,服务层的好处是它可以将来自一个或多个存储库的多个简单操作聚合到一个暴露域模型的单个操作中。示例:

private readonly ICustomerService _service;
public CustomerController(ICustomerService service)

    _service = service;


[AutoMap(typeof(Customer), typeof(CustomerViewModel))]
public ActionResult Create(int id)

    Customer customer = _service.GetCustomer(id);
    return View(customer);

在此示例中,AutoMap 是我可以编写的自定义操作过滤器,它在控制器操作之后执行,检查返回的对象并使用定义的 AutoMapper 映射将其映射到指定的目标类型。所以视图获取了对应的 CustomerViewModel 作为模型类型。相当于:

public ActionResult Create(int id)

    Customer customer = _service.GetCustomer(id);
    CustomerViewModel vm = Mapper.Map<Customer, CustomerViewModel>(customer);
    return View(vm);

只是可能集中的管道和重复代码太多。

我还建议您观看 Jimmy Bogard 的 putting your controllers on a diet video

【讨论】:

谢谢达林。我看过有人演示了一个类似的 AutoMap 属性——我看到 Jimmy 有一个可用的属性。您如何为这些视图模型的客户端验证(或者不是?)并在它们之间共享验证逻辑?然后您是否将您的存储库注入您的服务层? @AdamTuliper,我使用 FluentValidation.NET 进行服务器端验证和简单的客户端验证方案(必需,大于日期,...)。对于更复杂的客户端验证场景(依赖属性,...),我决定是否需要在客户端上处理它,如果需要为这些场景执行客户端验证,我编写自定义 jquery 验证附加程序。就服务层的存储库而言,是的,服务层使用 ctor 注入使所有存储库都允许对域模型执行简单的 CRUD 操作。 谢谢达林,另一个很好的答案:) 当我查看以上内容时,我想到了一个问题。当您的模型需要包含一个列表时,例如 IEnumerable,您如何处理从客户域对象到下拉列表的映射?如果我们使用实体框架,我可以看到在您的视图模型中定义了 Customer.States,但似乎在将 id 映射回实体时,如果您需要,这些字段不会跳动。 ex StateId - 所以您的 ViewModel 需要一组状态,但实际的实体只有 StateId。在这里我们似乎不能使用一个好的自动映射属性? 我喜欢service 解决方案,但我觉得这个例子有点过于简单了。在实际情况下,您不仅需要返回客户对象,还需要返回一些其他内容,例如产品或类别等。在这种情况下你的方法是什么?您是否创建了一个特定的服务方法来返回一个更复杂的模型,以便为该特定视图扁平化为 ViewModel?【参考方案2】:

我刚刚完成了一个项目,我们在 #4 上做了一个变体。我们将一个服务类注入到控制器中。服务类依赖于存储库和模型构建器类(我们称之为模型工厂)。

控制器调用服务类,处理业务验证逻辑,然后从适当的工厂获取视图模型。模型本身依赖数据注释进行输入验证。

它对我们的团队非常有效。有足够的关注点分离,让开发人员可以在不影响彼此的情况下完成他们的工作,但它足够易于管理以了解正在发生的事情。

这是我们第一次尝试,我们会坚持下去。我很想看看其他人的反应。

【讨论】:

谢谢。所以你的服务类又被注入了存储库?你的方法设置是什么?在服务类?您是如何跨多个但相似的视图模型处理客户端验证的? (例如编辑与创建) 跟下面Darin的例子很像,我们有个疯狂的需求,不能用开源软件,所以不能用Automapper。下次我们将用 automapper 替换视图模型工厂。我们使用 Display 和 Editor 模板为同一模型的不同视图提供服务,数据注释为我们提供了所需的所有验证。 模型构建器类从构造函数参数接收存储库对象(服务处理存储库对象)或模型构建器直接使用存储库本身?你能提供一些非常简单的例子吗?【参考方案3】:

我们的方法是将存储库注入控制器并使用 Automapper http://automapper.org/ 将其映射到 ViewModel。我们的 ViewModel 包含数据注释属性,以允许在客户端上进行验证。

我们在存储库上调用返回域对象(实体框架)的方法。域对象映射到 ViewModel。我们倾向于使用相同的 ViewModel 进行编辑和添加,因此只需要一次数据注释。最简单的形式如下所示:

    public ActionResult List(int custId, int projId)
    
        var users = _userRepository.GetByCustomerId(custId);
        var userList = Mapper.Map<IEnumerable<CMUser>, IEnumerable<UserListViewModel>>(users);
        return View(userList);
    

【讨论】:

但存储库未映射到视图模型。必须在存储库中调用某些方法以返回域对象或视图模型。如果您为每个模型或共享位置等使用单独的注释,这也不包括在内。 另外,如果使用相同的 ViewModel 进行编辑/添加,那么对于数据注释默认需要整数 ID 字段,您会做什么?如果表单中缺少,它将失败验证。默认情况下需要整数,因此您必须为“创建”场景初始化为 0,不是吗?【参考方案4】:

我使用了一个服务层,它对控制器隐藏域模型,从服务方法返回 ViewModel。这允许我在不影响客户端的情况下对域模型进行更改。

【讨论】:

以上是关于你是如何填充/验证你的 ViewModel 的?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 ViewModel 对象填充选择器,设置为第一个元素的初始状态并处理选择选择器项的操作

如何使用从数据库预填充的 vuejs 字段进行验证?

Codeigniter:如何构建使用表单验证和重新填充的编辑表单?

Flutter ViewModel 旧数据正在重新填充(使用 Provider)

ViewModel 中的 XML 到 LINQ 以填充模型

SwiftUI 从 ViewModel 预填充 TextField