Пользовательская проверка допустимости модели зависимых свойств с помощью Аннотирований данных

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

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

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

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?


ОБНОВЛЕНИЕ:

Кажется, что FluentValidation уже поддерживает клиентскую проверку и метаданные в ASP.NET MVC 2.

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

43
задан Darin Dimitrov 17 February 2010 в 16:08
поделиться

3 ответа

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" вызывается для вашего настраиваемого атрибута, ему передается весь экземпляр модели, чтобы таким образом можно было получить значения зависимых свойств. Вы можете легко следовать этому шаблону, чтобы создать атрибут сравнения дат или даже более общий атрибут сравнения.

У Брэда Уилсона есть хороший пример в своем блоге , показывающий, как добавить клиентскую часть проверки, хотя я не уверен, будет ли этот пример работать как в .NET 3.5, так и в .NET 4.0. .

29
ответ дан 26 November 2019 в 23:04
поделиться

Это результат типа курсора, используемого для доступа к данным, эта запись охватывает проблему и возможные исправления.

http://www.devx.com/tips/Tip/14143

EDIT

Прошу прощения за то, что не был более внимательным к тому факту, что вы имели дело с Compact. В случае Compact ситуация аналогична ситуации, на которую ссылается я, поскольку по умолчанию используются только прямые курсоры (которые не поддерживают подсчет строк), но имеются два других типа курсора, как описано в ссылке ниже.

http://support.microsoft.com/kb/272067

-121--3320616-

Fwiw, ни в одном примере не используются стандартные соглашения .NET. Дженерик EventHandler < T > должен объявлять событие:

public event EventHandler<EmployeeEventArgs> Leave;

Префикс "On" должен быть зарезервирован для защищенного метода, который вызывает событие:

protected virtual void OnLeave(EmployeeEventArgs e) {
    var handler = Leave;
    if (handler != null) handler(this, e);
}

У вас нет , чтобы сделать это таким образом, но любой будет мгновенно распознавать шаблон, понимать ваш код и знать, как его использовать и настраивать.

И это имеет большое преимущество, поскольку не приходится выбирать между пользовательским объявлением делегата и Действием < > , EventHandler < > является лучшим способом. Что отвечает на ваш вопрос.

-121--988033-

Поскольку методы DataAnnotations 3,5 .NET не позволяют предоставить действительный проверенный объект или контекст проверки, для этого придется проделать большую работу. Я должен признать, что я не знаком с 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);
}

Это полезно?

3
ответ дан 26 November 2019 в 23:04
поделиться

У меня была именно эта проблема, и я недавно открыл исходный код своего решения: http://foolproof.codeplex.com/

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

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

    [Required]
    [GreaterThan("StartDate")]
    public DateTime? EndDate { get; set; }
}
14
ответ дан 26 November 2019 в 23:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: