xVal 如何验证复杂类型的子属性?

发布于 2024-08-04 03:28:59 字数 2267 浏览 6 评论 0原文

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

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 不会遍历复杂的子属性。此外,如果这些错误被添加到错误集合中,它们在添加到模型状态时仍然需要正确添加前缀。例如。人员错误是这样添加的:

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

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

I'm using xVal in my ASP.NET MVC application, which is great in general. Following Steve Sanderson's blog post, I created a DataAnnotationsValidationRunner to do server-side validation of attributed objects. This works great for a simple class. e.g. Person:

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; }
}

However, if I add an Address property to this person, and mark the Address class with DataAnnotation attributes, they will not be validated. e.g.

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; }
}

One problem is that the DataAnnotationValidationRunner doesn't walk down the complex child properties. Also, if those errors are added to an errors collection, they still need to get prefixed correctly when added to the model state. For example. The Person errors are added like this:

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

I think the Address rules exceptions would need to be prefixed with "person.address". Is there a supported way of handling child object validation with xVal, or would creating a flattened data transfer object be the only solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

初心未许 2024-08-11 03:28:59

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

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

您可能指的是 Brad Wilson 的 DataAnnotationModelBinder?如果是这样,它确实应该验证复杂的 ViewModel 直至最后一个属性。如果没有,请尝试使用它来代替您正在使用的 DataAnnoationsModelRunner。此博客文章关于 使用 xVal 进行客户端验证展示了如何操作。

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

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

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

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

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

First of all, you need to differ between the Steve Sanderson's DataAnnotationsModelBinder and

Regarding your first question ("DataAnnotationValidationRunner doesn't walk down the complex child properties"):

Are you perhaps referring to Brad Wilson's DataAnnotationModelBinder? If so, it really should validate complex ViewModels down to the last properties. If not, try using that instead of the DataAnnoationsModelRunner you are using. This blog article blog article on Client-Side Validation with xVal shows how.

The first version of the DataAnnotationModelBinder had a bug that would make it crash when used with complex viewmodels. Perhaps there is a new version out which fixes the crash but ignores complex models?

In any case, I'd suggest giving the version of the DataAnnotationModelBinder used in the demo project of the blog article linked above a try. I am using it in my own real-world project and it does work on complex viewmodels.

Regarding your second question "Is there a supported way of handling child object validation with xVal":

You haven't posted any code residing on ASPX forms, but you might also be referring to the fact that a <%= Html.ClientSideValidation()%> only adds client-side validation to immediate properties of that Model Type, but not properties of child objects. You can simply circumvent the problem by using multiple ClientSideValidation statements, for example:

<%= Html.ClientSideValidation<ModelType>()%>
<%= Html.ClientSideValidation<ChildModelType>("ChildModelPropertyName")%>
内心旳酸楚 2024-08-11 03:28:59

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

我希望这一点很清楚。我一直在域对象上测试这个系统,到目前为止一切顺利。到处解决了一些问题,但事实证明这个想法对于我正在做的事情来说是合理的。

I had the same problem. I needed to validate complex objects that can appear as a property of another object. I haven't gotten into client side validation (yet), but the idea from Adrian Grigore about multiple html.ClientSideValidation() seems like it may be the ticket there.

I ended up creating a marker interface that marks all the classes that I need to validate. It could be an attribute or you could use this idea for all properties of a class.

Basically it validates the object using the DataAnnotationsValidationRunner you mention above and then iterates over the properties of the object and runs the DataAnnotationsValicationRunner over all of those until there are no more to check.

Here's pseudo code for what I did:

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;
}

I hope this is clear. I've been testing this system on domain objects and so far so good. Working out a few kinks here and there, but the idea has proven sound for what I'm doing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文