xVal Как проверить дочерние свойства сложных типов?

Я использую xVal в своем приложении ASP.NET MVC, что в целом отлично. После сообщения в блоге Стива Сандерсона, я создал 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 и помечу класс Address атрибутами DataAnnotation, они не будут проверены. например

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, или создание плоского объекта передачи данных будет единственным решением?


person Lance Fisher    schedule 02.09.2009    source источник


Ответы (2)


Прежде всего, вам нужно различать DataAnnotationsModelBinder Стива Сандерсона и

Относительно вашего первого вопроса («DataAnnotationValidationRunner не просматривает сложные дочерние свойства»):

Вы, возможно, имеете в виду DataAnnotationModelBinder Брэда Уилсона? Если это так, он действительно должен проверять сложные ViewModels вплоть до последних свойств. Если нет, попробуйте использовать его вместо DataAnnoationsModelRunner, который вы используете. Эта статья в блоге Статья в блоге на Проверка на стороне клиента с помощью xVal показывает, как это сделать.

В первой версии DataAnnotationModelBinder была ошибка, которая приводила к сбою при использовании со сложными моделями просмотра. Возможно, есть новая версия, которая устраняет сбой, но игнорирует сложные модели?

В любом случае я предлагаю попробовать версию DataAnnotationModelBinder, используемую в демонстрационном проекте статьи блога, указанной выше. Я использую его в своем собственном реальном проекте, и он действительно работает со сложными моделями просмотра.

Относительно вашего второго вопроса «Есть ли поддерживаемый способ обработки проверки дочернего объекта с помощью xVal»:

Вы не публиковали код, размещенный в формах ASPX, но вы также можете иметь в виду тот факт, что ‹% = Html.ClientSideValidation ()%> только добавляет проверку на стороне клиента к непосредственным свойствам этого типа модели, но не к свойствам дочерних объектов. Вы можете просто обойти проблему, используя несколько операторов ClientSideValidation, например:

<%= Html.ClientSideValidation<ModelType>()%>
<%= Html.ClientSideValidation<ChildModelType>("ChildModelPropertyName")%>
person Adrian Grigore    schedule 03.09.2009
comment
Второй ответ работает, но не работает, если у вас есть несколько свойств родительского объекта, которые все связывают один и тот же дочерний тип, но с разными свойствами. т.е. User BillingAddress ShippingAddress Похоже, вы могли бы настроить утилиту проверки xval.jquery, чтобы исправить это. - person Sean Chambers; 21.02.2010

У меня такая же проблема. Мне нужно было проверить сложные объекты, которые могут появляться как свойство другого объекта. Я не прошел проверку на стороне клиента (пока), но идея Адриана Григоре о нескольких 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;
}

Надеюсь, это понятно. Я тестировал эту систему на объектах домена и пока все хорошо. Поправляю кое-какие изюминки здесь и там, но идея оказалась правильной для того, что я делаю.

person Rob Sutherland    schedule 05.09.2009