2010-02-17 2 views
41

С тех пор я использовал отличную библиотеку FluentValidation для проверки моих классов моделей. В веб-приложениях я использую его вместе с плагином jquery.validate для проверки валидации на стороне клиента. Один из недостатков заключается в том, что большая часть логики проверки повторяется на стороне клиента и больше не централизована в одном месте.Пользовательская проверка модели зависимых свойств с использованием аннотаций данных

По этой причине я ищу альтернативу. Имеются примеры many из there, показывающие использование аннотаций данных для выполнения проверки модели. Это выглядит очень многообещающе. Одна вещь, которую я не мог узнать, - это проверить свойство, которое зависит от другого значения свойства.

Давайте возьмем для примера следующую модель:

public class Event 
{ 
    [Required] 
    public DateTime? StartDate { get; set; } 
    [Required] 
    public DateTime? EndDate { get; set; } 
} 

Я хотел бы, чтобы гарантировать, что EndDate больше StartDate. Я мог бы написать собственный атрибут проверки подлинности , расширяющий ValidationAttribute, чтобы выполнить пользовательскую логику проверки. К сожалению, я не мог найти способ, чтобы получить экземпляр модели:

public class CustomValidationAttribute : ValidationAttribute 
{ 
    public override bool IsValid(object value) 
    { 
     // value represents the property value on which this attribute is applied 
     // but how to obtain the object instance to which this property belongs? 
     return true; 
    } 
} 

я обнаружил, что CustomValidationAttribute, кажется, делает эту работу, потому что он обладает этим свойством ValidationContext, который содержит экземпляр объекта проверяемого. К сожалению, этот атрибут был добавлен только в .NET 4.0. Поэтому мой вопрос: могу ли я достичь той же функциональности в .NET 3.5 SP1?


UPDATE:

Кажется, что FluentValidation already supports клиентской валидации и метаданных в ASP.NET MVC 2.

Тем не менее, было бы хорошо знать, хотя, если аннотации данных могут быть использованы для проверки зависимых свойств ,

+0

Есть ли у вас или кто-либо выяснил способ получения данных и обработки FluentValidation (для проверки) вместе в одном классе/модели? если бы это было фантастично, у меня есть тема об этом обсуждении с автором FV Джереми, вы можете посмотреть здесь: http://fluentvalidation.codeplex.com/Thread/View.aspx?ThreadId=212371 –

ответ

28

MVC2 поставляется с образцом «PropertiesMustMatchAttribute», который показывает, как заставить DataAnnotations работать для вас, и он должен работать как в .NET 3.5, так и в .NET 4.0.Этот пример кода выглядит следующим образом:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] 
public sealed class PropertiesMustMatchAttribute : ValidationAttribute 
{ 
    private const string _defaultErrorMessage = "'{0}' and '{1}' do not match."; 

    private readonly object _typeId = new object(); 

    public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty) 
     : base(_defaultErrorMessage) 
    { 
     OriginalProperty = originalProperty; 
     ConfirmProperty = confirmProperty; 
    } 

    public string ConfirmProperty 
    { 
     get; 
     private set; 
    } 

    public string OriginalProperty 
    { 
     get; 
     private set; 
    } 

    public override object TypeId 
    { 
     get 
     { 
      return _typeId; 
     } 
    } 

    public override string FormatErrorMessage(string name) 
    { 
     return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, 
      OriginalProperty, ConfirmProperty); 
    } 

    public override bool IsValid(object value) 
    { 
     PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value); 
     object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value); 
     object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value); 
     return Object.Equals(originalValue, confirmValue); 
    } 
} 

При использовании этого атрибута, а не поставить его на свойство вашей модели класса, вы положили его на сам класс:

[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")] 
public class ChangePasswordModel 
{ 
    public string NewPassword { get; set; } 
    public string ConfirmPassword { get; set; } 
} 

Когда «IsValid «получает вызов по вашему пользовательскому атрибуту, весь экземпляр модели передается ему, поэтому вы можете получить значения зависимых свойств таким образом. Вы можете легко следовать этому шаблону, чтобы создать атрибут сравнения даты или даже более общий атрибут сравнения.

Brad Wilson has a good example on his blog, в котором показано, как добавить часть проверки на стороне клиента, хотя я не уверен, что этот пример будет работать как в .NET 3.5, так и в .NET 4.0.

+2

Я пробовал это, но я никогда не могу получить ошибку проверки для отображения на моих ASPX-страницах/представлениях. Я попытался вызвать validationmessage для использования пустой строки, также попытался использовать сводку проверки, и он не отображается там (как в примере propertymustmatch) –

+1

Я потратил несколько часов на то, чтобы получить эту работу и подумал, что мой код был неправильным, пока я наконец не понял, что просто не тестировал его, когда увидел этот пост: http://stackoverflow.com/questions/3586324/custom-validation-attribute-is-not-called-asp-net-mvc (в основном сначала проверяется проверка на уровне поля/свойства, поэтому вам нужны те, которые должны быть полностью проверены, прежде чем он сгорит ваш метод isvalid() атрибута уровня класса). – jimasp

+0

Позже я нашел лучшее сообщение о проверке класса после проблемы проверки поля здесь: http://stackoverflow.com/questions/3099397/property-level-validation-errors-hinder-the-validation-of-class-level-validation – jimasp

3

Поскольку методы DataAnnotations .NET 3.5 не позволяют вам предоставлять фактический объект, проверенный или используемый для проверки, вам нужно будет немного обмануть, чтобы выполнить это. Должен признаться, что я не знаком с ASP.NET MVC, поэтому не могу сказать, как это сделать в точности с MCV, но вы можете попробовать использовать статическое значение потока для передачи самого аргумента. Вот пример с чем-то, что может сработать.

Сначала нужно создать своего рода «объекта сферы», который позволяет передавать объекты вокруг, не передавать их через стек вызовов:

public sealed class ContextScope : IDisposable 
{ 
    [ThreadStatic] 
    private static object currentContext; 

    public ContextScope(object context) 
    { 
     currentContext = context; 
    } 

    public static object CurrentContext 
    { 
     get { return context; } 
    } 

    public void Dispose() 
    { 
     currentContext = null; 
    } 
} 

Далее создайте свой валидатор использовать ContextScope:

public class CustomValidationAttribute : ValidationAttribute 
{ 
    public override bool IsValid(object value) 
    { 
     Event e = (Event)ObjectContext.CurrentContext; 

     // validate event here. 
    } 
} 

И последнее, но не в последнюю очередь, убедитесь, что объект мимо вокруг через ContextScope:

Event eventToValidate = [....]; 
using (var scope new ContextScope(eventToValidate)) 
{ 
    DataAnnotations.Validator.Validate(eventToValidate); 
} 

Это полезно?

+0

Стивен, это выглядит красиво. Единственное, что я хотел бы изменить, - это сохранить текущий контекст в HttpContext вместо использования ThreadStatic. Я бы просто избегал этого в приложении ASP.NET. –

+0

Можете ли вы объяснить, почему вы считаете, что нам следует избегать этого в приложении ASP.NET.Я использую эту конструкцию в своих собственных приложениях, поэтому мне очень интересно, почему это плохо. – Steven

+4

Есть много статей в Интернете, почему это плохо. Вот один из них: http://www.hanselman.com/blog/ATaleOfTwoTechniquesTheThreadStaticAttributeAndSystemWebHttpContextCurrentItems.aspx. Проблема с ThreadStatic заключается в том, что в ASP.NET у вас нет контроля над ресурсом потока и при повторном использовании потоков существуют обстоятельства, при которых переменная может получить изменения. Все становится еще более уродливым, если вы используете асинхронные страницы и контроллеры. Например, запрос может начинаться с потока и заканчиваться другим потоком. Таким образом, в ASP.NET ** только ** способ иметь истинное хранилище для каждого запроса - это HttpContext. –

14

Я имел эту саму проблему и недавно открытым исходным кодом мое решение: http://foolproof.codeplex.com/

решение Foolproof на приведенном выше примере будет:

public class Event 
{ 
    [Required] 
    public DateTime? StartDate { get; set; } 

    [Required] 
    [GreaterThan("StartDate")] 
    public DateTime? EndDate { get; set; } 
} 
+0

Я думаю, что проверка даты GreaterThan работает только с датами формата US – GraemeMiller

7

Вместо PropertiesMustMatch в CompareAttribute, которые могут быть использованы в MVC3 , По этой ссылке http://devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-1:

public class RegisterModel 
{ 
    // skipped 

    [Required] 
    [ValidatePasswordLength] 
    [DataType(DataType.Password)] 
    [Display(Name = "Password")] 
    public string Password { get; set; }      

    [DataType(DataType.Password)] 
    [Display(Name = "Confirm password")] 
    [Compare("Password", ErrorMessage = "The password and confirmation do not match.")] 
    public string ConfirmPassword { get; set; } 
} 

CompareAttribute является новым, очень полезно валидатор, который не является на самом деле часть System.ComponentModel.DataAnnotations, но был добавлен к System.Web .Mvc DLL от команды. Хотя не особенно хорошо им (только сравнения он делает это, чтобы проверить равенства, так что, возможно, EqualTo бы более очевидным), легко видеть из использования, что этот валидатор проверяет , что стоимость одного свойства равна значение другого имущества. Вы можете видеть из кода, что атрибут занимает строковое свойство, которое является именем другого имущества, которое вы сравниваете. Классическое использование этого типа валидатора - это то, что мы используем здесь: .

3

Прошло некоторое время, так как ваш вопрос был задан, но если вы все-таки как метаданные (по крайней мере, иногда), ниже есть еще одно альтернативное решение, что позволяет обеспечить различные логические выражения для атрибутов:

[Required] 
public DateTime? StartDate { get; set; }  
[Required] 
[AssertThat("StartDate != null && EndDate > StartDate")] 
public DateTime? EndDate { get; set; } 

Он работает как на сервере, так и на стороне клиента. Подробнее can be found here.

+0

Большое вам спасибо, что эта библиотека очень полезна для большинства вещей. – Enzero

Смежные вопросы