Asp.net mvc 使用视图模型时的MVC.net EF验证

Asp.net mvc 使用视图模型时的MVC.net EF验证,asp.net-mvc,validation,Asp.net Mvc,Validation,我正在开发一个MVC应用程序,并试图实现一些验证。我已经构建了一个使用EF存储的站点和一组带有automapper的视图模型 我想添加一些验证,如果我将其添加到视图模型中,我相信这些验证会起作用,但是我假设最好将验证与EF模型一起添加,因此如果将来我创建另一个接口,同样的验证也会应用 首先,这是正确的方法,其次,我如何让MVC在保存对象之前实际测试验证。目前它只是跳过我的EF验证 地址模型是自动生成的,因此我创建了这个分部类来添加验证: public partial class Address

我正在开发一个MVC应用程序,并试图实现一些验证。我已经构建了一个使用EF存储的站点和一组带有automapper的视图模型

我想添加一些验证,如果我将其添加到视图模型中,我相信这些验证会起作用,但是我假设最好将验证与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 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);
}
public ActionResult AddAddress(AddressVM)
{
附带地址theAddress=Mapper.Map(vm);
if(ModelState.IsValid)
{
地址添加(地址);
UOW.Save();
}
返回PartialView(“AddressVM编辑”,vm);
}
对于您的对象来说,这始终是正确的,因为它将查找您的模型的有效性,即AddressVM(您从视图中收到它,所以这是您的模型),并且没有任何验证器。ModelState不知道您已将此对象映射到实现验证的其他对象。您需要手动对其他对象运行验证,并将验证错误添加到ModelState

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

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);
    }
} 
公共类地址vm:IValidatableObject
{ 
公共int?ID{get;set;}
[显示(Name=“地址行1”)]
公共字符串AddressLine1{get;set;}
[显示(Name=“地址行2”)]
公共字符串AddressLine2{get;set;}
[显示(Name=“地址行3”)]
公共字符串AddressLine3{get;set;}
[显示(Name=“Town”)]
公共字符串{get;set;}
[显示(Name=“City”)]
公共字符串City{get;set;}
[显示(Name=“County”)]
公共字符串country{get;set;}
[显示(Name=“Postcode”)]
公共字符串邮政编码{get;set;}
////我在类定义中添加了这个和接口:
公共IncidentAddress GetIncidentAddress()
{ 
返回Mapper.Map(this);
}
公共IEnumerable验证(ValidationContext ValidationContext)
{
返回此.GetIncidentAddress().Validate(validationContext);
}
} 

这样,您的逻辑保留在您的业务对象中,并且您的viewmodel使用它时没有它的副本或其他依赖项。

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

我在考虑两个变通办法

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


    如果您需要可重用的验证器类,您可以签出。它对asp.net-mvc也有很好的支持

    MVC:Controller=MVVM:ViewModel
    if (ModelState.IsValid)
    
    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);
        }
    }