1

我在我的 ASP.NET MVC 应用程序中使用xVal,这通常很棒。在Steve Sanderson 的博客文章之后,我创建了一个 DataAnnotationsValidationRunner 来对属性对象进行服务器端验证。这对于一个简单的类非常有用。例如人:

public static class DataAnnotationsValidationRunner
{
    public static IEnumerable<ErrorInfo> GetErrors(object o)
    {
        return from prop in TypeDescriptor.GetProperties(o).Cast<PropertyDescriptor>()
               from attribute in prop.Attributes.OfType<ValidationAttribute>()
               where !attribute.IsValid(prop.GetValue(o))
               select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), o);
    }
}

public class Person
{
    [Required(ErrorMessage="Please enter your first name")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "Please enter your last name")]
    public string LastName { get; set; }
}

但是,如果我向这个人添加一个 Address 属性,并用 DataAnnotation 属性标记 Address 类,它们将不会被验证。例如

public class Person
{
    [Required(ErrorMessage="Please enter your first name")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "Please enter your last name")]
    public string LastName { get; set; }

    public Address Address { get; set; }
}

public class Address 
{
    [Required(ErrorMessage="Please enter a street address")]
    public string Street { get; set; }

    public string StreetLine2 { get; set; }

    [Required(ErrorMessage = "Please enter your city")]
    public string City { get; set; }

    [Required(ErrorMessage = "Please enter your state")]
    public string State { get; set; }

    [Required(ErrorMessage = "Please enter your zip code")]
    public string Zip { get; set; }

    public string Country { get; set; }
}

一个问题是 DataAnnotationValidationRunner 不会遍历复杂的子属性。此外,如果将这些错误添加到错误集合中,则在添加到模型状态时仍需要正确添加前缀。例如。Person 错误是这样添加的:

    catch (RulesException ex)
    {
        ex.AddModelStateErrors(ModelState, "person");
    }

我认为地址规则例外需要以“person.address”为前缀。是否有支持使用 xVal 处理子对象验证的方法,或者创建扁平数据传输对象是唯一的解决方案?

4

2 回答 2

2

首先,您需要区分 Steve Sanderson 的 DataAnnotationsModelBinder 和

关于您的第一个问题(“DataAnnotationValidationRunner 不会遍历复杂的子属性”):

您可能指的是 Brad Wilson 的 DataAnnotationModelBinder 吗?如果是这样,它真的应该验证复杂的 ViewModel 直到最后的属性。如果没有,请尝试使用它而不是您正在使用的 DataAnnoationsModelRunner。这篇关于使用 xVal 进行客户端验证的博客文章展示了如何进行。

DataAnnotationModelBinder 的第一个版本有一个错误,当与复杂的视图模型一起使用时会导致它崩溃。也许有一个新版本可以修复崩溃但忽略复杂模型?

无论如何,我建议尝试使用上面链接的博客文章的演示项目中使用的 DataAnnotationModelBinder 版本。我在我自己的实际项目中使用它,它确实适用于复杂的视图模型。

关于您的第二个问题“是否有支持使用 xVal 处理子对象验证的方法”

您尚未发布任何驻留在 ASPX 表单上的代码,但您可能还指的是 <%= Html.ClientSideValidation()%> 仅将客户端验证添加到该模型类型的即时属性,而不是属性子对象。您可以通过使用多个 ClientSideValidation 语句来简单地规避该问题,例如:

<%= Html.ClientSideValidation<ModelType>()%>
<%= Html.ClientSideValidation<ChildModelType>("ChildModelPropertyName")%>
于 2009-09-03T09:29:04.367 回答
1

我有同样的问题。我需要验证可以作为另一个对象的属性出现的复杂对象。我还没有进入客户端验证(还),但是 Adrian Grigore 关于多个 html.ClientSideValidation() 的想法似乎可能是那里的票。

我最终创建了一个标记接口来标记我需要验证的所有类。它可以是一个属性,或者您可以将这个想法用于类的所有属性。

基本上,它使用您上面提到的 DataAnnotationsValidationRunner 验证对象,然后迭代对象的属性并在所有这些属性上运行 DataAnnotationsValicationRunner ,直到没有更多要检查的内容。

这是我所做的伪代码:

IEnumarable<ValidationError> GetErrors(object instance) {
    List<ValidationError> errors = new List<ValidationError>();
    errors.AddRange(GetDataAnnotationErrors(instance));
    errors.AddRange(GetPropertyErrors(instance));
    return errors;
}
IEnumerable<ValidationError> GetDataAnnotationErrors(object instance) {
    // code very similar to what you have above
}
IEnumearable<ValidationError> GetPropertyErrors(object instance)
{
     var errors = new List<ValidationError>();
     var objectsToValidate = instance.GetType().GetProperties().Where(p => p.PropertyType.GetInterface().Contains(typeof(IMarkerInterface)));
     // the call above could do any type of reflecting over the properties you want
     // could just check to make sure it isn't a base type so that all custom 
     // object would be checked
     if(objectsToValidate == null) return errors;
     foreach(object obj in objectsToValidate)
     {
          errors.AddRange(GetDataAnnotationErrors(obj));
          errors.AddRange(GetPropertyErrors(obj));
     }
     return errors;
}

我希望这很清楚。我一直在域对象上测试这个系统,到目前为止一切都很好。在这里和那里解决一些问题,但事实证明这个想法对我正在做的事情是合理的。

于 2009-09-05T02:18:25.900 回答