用MVC清理和验证ViewModel,怎么样?

Fel*_*ani 4 c# nhibernate asp.net-mvc domain-driven-design viewmodel

我有一些问题,关于如何以最好的asp.net mvc实现DDD原则的最佳方式.实际上,我想知道,你是如何进行验证的(在viewmodel或model中)?

我有这个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; } 
}
Run Code Online (Sandbox Code Playgroud)

好.该模型使用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");
}
Run Code Online (Sandbox Code Playgroud)

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

Dar*_*rov 9

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

那么让我们举一个典型的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");
}
Run Code Online (Sandbox Code Playgroud)

如果您想处理域模型错误,则有不同的可能性.我个人喜欢这种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);
}
Run Code Online (Sandbox Code Playgroud)

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

就视图模型中的验证而言,存在不同的方式.您可以使用Microsoft的官方方式,即使用验证属性修饰视图模型属性.例如:

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

    ...
}
Run Code Online (Sandbox Code Playgroud)

我个人不使用这些属性.我发现它们非常有限,特别是当你想要使用依赖属性进行更复杂的验证时等等.出于这个原因,我使用FluentValidation.NET,它非常好地与ASP.NET MVC 集成,并允许我轻松地单元测试我的验证规则.

  • 不会将`ModelState`传递给业务层是漏洞抽象,因为它是MVC特定的类? (2认同)