使用View Models时的MVC.net EF验证

时间:2011-09-26 12:03:09

标签: asp.net-mvc validation

我正在研究MVC应用程序,我正在尝试实现一些验证。我已经建立了网站使用EF存储和一组使用automapper的视图模型。

我想添加一些验证,如果我将它添加到View Models中我肯定会有效但是我假设将验证放入EF模型会更好,所以如果将来我创建另一个界面相同的验证也适用。

首先是这是正确的方法,其次是如何在保存对象之前让MVC实际测试验证。目前它只是跳过我的EF验证。

地址模型是自动生成的,所以我创建了这个部分类来添加验证:

public partial class Address : IValidatableObject
{
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (!string.IsNullOrWhiteSpace(this.AddressLine1) &&
            !string.IsNullOrWhiteSpace(this.AddressLine2) &&
            !string.IsNullOrWhiteSpace(this.AddressLine3) &&
            !string.IsNullOrWhiteSpace(this.Town) &&
            !string.IsNullOrWhiteSpace(this.City) &&
            !string.IsNullOrWhiteSpace(this.County) &&
            !string.IsNullOrWhiteSpace(this.Postcode))
            yield return new ValidationResult("Address cannot be blank.");
    }
}

这是我的视图模型类,显示名称已更改

public class AddressVM
{
    public int? ID { get; set; }

    [Display(Name = "Address line 1")]
    public string AddressLine1 { get; set; }

    [Display(Name = "Address line 2")]
    public string AddressLine2 { get; set; }

    [Display(Name = "Address line 3")]
    public string AddressLine3 { get; set; }

    [Display(Name = "Town")]
    public string Town { get; set; }

    [Display(Name = "City")]
    public string City { get; set; }

    [Display(Name = "County")]
    public string County { get; set; }

    [Display(Name = "Postcode")]
    public string PostCode { get; set; }
}

这是我的控制器

public ActionResult AddAddress(AddressVM vm)
{
    IncidentAddress theAddress = Mapper.Map<AddressVM, Address>(vm);
    if (ModelState.IsValid)
    {
        UOW.Addresses.Add(theAddress);
        UOW.Save();
    }
    return PartialView("AddressVM-edit", vm);
}

2 个答案:

答案 0 :(得分:1)

if (ModelState.IsValid)

对于您的对象,这将始终如此,因为它将查找模型的有效性,即AddressVM(您从视图中收到,因此这是您的模型)并且没有任何验证器。 ModelState不知道您已将此对象映射到实现验证的其他对象。您需要手动对其他对象运行验证,并将验证错误添加到ModelState。

如果要将它分开,可以在AddressVM上实现IValidatableObject,并在内部通过创建Address实例,从AddressVM(this)映射并返回其Validate方法的结果来执行验证。您还可以将相同构造的Address对象公开为属性,并使用它来执行实体操作。

AddressVM示例:

public class AddressVM : IValidatableObject     
{ 
    public int? ID { get; set; } 

    [Display(Name = "Address line 1")] 
    public string AddressLine1 { get; set; } 

    [Display(Name = "Address line 2")] 
    public string AddressLine2 { get; set; } 

    [Display(Name = "Address line 3")] 
    public string AddressLine3 { get; set; } 

    [Display(Name = "Town")] 
    public string Town { get; set; } 

    [Display(Name = "City")] 
    public string City { get; set; } 

    [Display(Name = "County")] 
    public string County { get; set; } 

    [Display(Name = "Postcode")] 
    public string PostCode { get; set; }


    //// I added this and interface in class definition:

    public IncidentAddress GetIncidentAddress()
    { 
        return Mapper.Map<AddressVM, Address>(this);   
    }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)      
    {
        return this.GetIncidentAddress().Validate(validationContext);
    }
} 

这样,您的逻辑就会停留在您的业务对象中,并且您的viewmodel会使用它而不会复制它或其他依赖项。

答案 1 :(得分:1)

Address类和AddressVm在您的情况下不会相互绑定 - AutoMapper不会执行验证,它只是复制值。因此,您不会执行ModelState填充和验证。

我正在考虑两种解决方法

  1. AddressVm上定义验证。如果是ModelState.IsValid,请将AddressVm映射到Address并保存。
  2. 您根本不需要AddressVm。更改操作签名以期望Address参数。这样,ModelState.IsValid将由验证系统自动填充(不是最佳解决方案)。
  3. 理想情况下,应针对特定方案定义ViewModel。在您的情况下,我将定义AddAddressModel,仅使用 来添加地址,并仅定义创建地址所需的属性。然后,在AddAddressModel上定义验证并使用mapper将ViewModel映射到Address实例(所以,我更喜欢第一个解决方案,再加上定义特定模型)。

    如果您需要可重复使用的验证程序类,可以查看FluentValidation。它也得到了asp.net-mvc的良好支持。