【问题标题】:Clean up and validating ViewModel with MVC, how?使用 MVC 清理和验证 ViewModel,如何?
【发布时间】:2012-05-09 13:53:34
【问题描述】:

我有一些关于如何使用 asp.net mvc 的最佳实践来实现 DDD 原则的最佳方式的问题。实际上,我想知道,您是如何进行验证的(在视图模型或模型中)?

我有这个 DomainModel 和 ViewModel:

public class Product {
   public int Id { get; protected set; }
   public string Name { get; set; }
   public decimal Price { get; set; }
   public int Stock { get; set; }
   public Category Category { get; set; }
   public Supplier Supplier { get; set; }
   public string Code { get; set; } // it has to be unique
}

public class ProductViewModel {
   public int Id { get; set; }
   /* other simple properties */
   public int IdCategory { get; set; }
   public int IdSupplier { get; set; }
   public string Code { get; set; } 
}

好的。该模型使用 NHibernate 映射并且工作正常。我想知道,是否更好地为 ViewModel 或 DomainModel 创建验证?我的意思是,当我在 asp.net mvc 的操作上收到 ViewModel 时,我会验证它,但是如果我在 viewmodel 上添加业务规则,我不会做错吗?我问这个是因为我知道最好将这些业务验证添加到我的域中,但是我应该在我的帖子上进行两次验证,然后再坚持吗?看看我在 asp.net mvc 上的操作:

[Post]
public ActionResult Update(ProductViewModel viewModel) {

  // what kind of validations should I do here? 
  if (invalid) {
    return View(viewModel);
  } 

  // how can I return the errors to my View?

  // Is here any best pratice to transform from ViewModel to Domain instance?
  Product product = ???

  _repository.Save(product);

  return RedirectToAction("Index");
}

有人可以通过代码做一个例子吗?

【问题讨论】:

    标签: c# asp.net-mvc nhibernate domain-driven-design viewmodel


    【解决方案1】:

    我会说您应该在两个级别上执行验证。在您的视图模型中,您将进行表面验证,例如此字段是必需的,并且此字段必须采用以下格式,而在您的业务层中,您应该验证业务规则,例如用户名已经存在,...

    让我们举个例子来看看典型的 POST 操作可能是什么样子:

    [HttpPost]
    public ActionResult Update(ProductViewModel viewModel) 
    {
        if (!ModelState.IsValid) 
        {
            // The view model is invalid => redisplay the view in order
            // to show the error messages
            return View(viewModel);
        }    
    
        // at this stage we know that the view model is valid =>
        // now we can map it to a domain model that we want to update:
    
        Product product = Repository.GetModel(viewModel.Id);
    
        // I use AutoMapper (http://automapper.org/) to map between 
        // my domain models and my view models. In this example
        // we are updating only the properties of the domain model
        // that were part of the view model and that the user is allowed
        // to modify in this view => we are merging
        Mapper.Map<ProductViewModel, Product>(viewModel, product);
    
        // now we can process the domain model
        Repository.Update(product);
    
        return RedirectToAction("Index");
    }
    

    如果您想处理域模型错误,则有不同的可能性。我个人喜欢TryXXX 模式:

    string errorMessage;
    if (!Repository.TryUpdate(product, out errorMessage))
    {
        // the business validation failed => add the error message to the modelstate
        // and redisplay the view
        ModelState.AddModelError("", errorMessage);
        return View(viewModel);
    }
    

    另一种可能是将ModelState字典传递给业务层,让它直接添加模型状态错误。这样您就可以再次简单地在视图中测试if (!ModelState.IsValid) 以了解业务层中是否出现问题并重新显示相同的视图以显示错误消息。

    就视图模型中的验证而言,有不同的方法。您可以使用 Microsoft 的 官方 方式,即使用验证属性装饰您的视图模型属性。例如:

    public class ProductViewModel 
    {
        [Required]
        public string Foo { get; set; }
    
        ...
    }
    

    我个人不使用这些属性。我发现它们非常有限,尤其是当您想对依赖属性等进行更复杂的验证时。出于这个原因,我使用 FluentValidation.NET 其中 integrates very nicely 与 ASP.NET MVC 并允许我轻松地 unit test 我的验证规则。

    【讨论】:

    • 感谢 Darin,作为示例,我的产品的 Code 属性(例如)就像用户名,每个产品都是独一无二的。如果业务模型有任何错误,我如何在映射后(从 VM 到 Domani)进行验证并将错误返回给 View?
    • @felipeoriani,正如我在回答中所说的那样,有不同的方法。一种方法是使用TryXXX 模式。另一种方法是将 ModelState 字典传递给业务层,以便它负责将错误消息直接添加到其中。
    • 是的,我稍后会尝试实现它,然后我会回来检查您的 aswser。谢谢达林...是的,我也更喜欢使用 FluentValidation =)
    • 我认为你的 if (ModelState.IsValid) 应该是 if (!ModelState.IsValid)
    • ModelState 传递给业务层不会是泄漏抽象,因为它是 MVC 特定的类吗?
    【解决方案2】:

    应用于您的视图模型的验证规则不一定与您的域模型相同。通常,您会检查视图模型是否存在输入错误,并检查域模型是否存在违反业务规则的情况。两个模型中的一些验证规则是相同的,这很好,因为您的域模型独立于视图模型,反之亦然。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-16
      • 2010-11-11
      相关资源
      最近更新 更多