【问题标题】:xVal How do I validate child properties of complex types?xVal 如何验证复杂类型的子属性?
【发布时间】:2009-09-02 19:56:46
【问题描述】:

我在我的 ASP.NET MVC 应用程序中使用了xVal,这通常很棒。在Steve Sanderson's blog post 之后,我创建了一个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 处理子对象验证的方法,或者创建扁平数据传输对象是唯一的解决方案?

【问题讨论】:

    标签: asp.net-mvc validation data-annotations xval


    【解决方案1】:

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

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

    您可能指的是 Brad Wilson 的 DataAnnotationModelBinder 吗?如果是这样,它真的应该验证复杂的 ViewModel 直到最后的属性。如果没有,请尝试使用它而不是您正在使用的 DataAnnoationsModelRunner。 Client-Side Validation with xVal 上的这篇博客文章博客文章展示了如何操作。

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

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

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

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

    <%= Html.ClientSideValidation<ModelType>()%>
    <%= Html.ClientSideValidation<ChildModelType>("ChildModelPropertyName")%>
    

    【讨论】:

    • 第二个答案确实有效,但如果您在父对象上有多个属性都关联相同的子类型,但具有不同的属性,则会崩溃。 IE。 User BillingAddress ShippingAddress 看起来你可以调整 xval.jquery 验证实用程序来解决这个问题。
    【解决方案2】:

    我遇到了同样的问题。我需要验证可以作为另一个对象的属性出现的复杂对象。我还没有进入客户端验证(还),但是 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;
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-30
      • 1970-01-01
      • 1970-01-01
      • 2021-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-02
      相关资源
      最近更新 更多