Невозможно установить имена членов из пользовательского атрибута проверки в MVC2 - PullRequest
9 голосов
/ 24 ноября 2010

Я создал настраиваемый атрибут проверки путем создания подкласса ValidationAttribute.Атрибут применяется к моей модели представления на уровне класса, так как он должен проверить более одного свойства.

Я переопределяю

protected override ValidationResult IsValid(object value, ValidationContext validationContext)

и возвращаю:

new ValidationResult("Always Fail", new List<string> { "DateOfBirth" }); 

во всех случаях, когда DateOfBirth является одним из свойств моей модели представления.

Когда я запускаю свое приложение, я вижу, что это становится хитом.ModelState.IsValid имеет значение false, но когда я проверяю содержимое ModelState, я вижу, что свойство DateOfBirth НЕ содержит ошибок.Вместо этого у меня есть пустая строка Key со значением null и исключение, содержащее строку, указанную в моем атрибуте проверки.

Это приводит к тому, что при использовании ValidationMessageFor не отображается сообщение об ошибке в моем пользовательском интерфейсе.Если я использую ValidationSummary, то я вижу ошибку.Это потому, что оно не связано со свойством.

Похоже, что оно игнорирует тот факт, что я указал имя члена в результате проверки.

Почему это так и как мне это сделать?исправить это?

ПРИМЕР КОДА ПО ЗАПРОСУ:

 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
    public class ExampleValidationAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // note that I will be doing complex validation of multiple properties when complete so this is why it is a class level attribute
            return new ValidationResult("Always Fail", new List<string> { "DateOfBirth" });
        }
    }

    [ExampleValidation]
    public class ExampleViewModel
    {
        public string DateOfBirth { get; set; }
    }

Ответы [ 4 ]

13 голосов
/ 13 января 2011

Привет всем.

Все еще ищете решение?

Я решил ту же проблему сегодня. Вы должны создать пользовательский атрибут проверки, который будет проверять 2 даты (пример ниже). Затем вам нужен адаптер (валидатор), который будет проверять модель с вашим пользовательским атрибутом. И последнее - это привязка адаптера с атрибутом. Может быть, какой-то пример объяснит это лучше меня :)

Вот и мы:

DateCompareAttribute.cs:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class DateCompareAttribute : ValidationAttribute
{
    public enum Operations
    {
        Equals,            
        LesserThan,
        GreaterThan,
        LesserOrEquals,
        GreaterOrEquals,
        NotEquals
    };

    private string _From;
    private string _To;
    private PropertyInfo _FromPropertyInfo;
    private PropertyInfo _ToPropertyInfo;
    private Operations _Operation;

    public string MemberName
    {
        get
        {
            return _From;
        }
    }

    public DateCompareAttribute(string from, string to, Operations operation)
    {
        _From = from;
        _To = to;
        _Operation = operation;

        //gets the error message for the operation from resource file
        ErrorMessageResourceName = "DateCompare" + operation.ToString();
        ErrorMessageResourceType = typeof(ValidationStrings);
    }

    public override bool IsValid(object value)
    {
        Type type = value.GetType();

        _FromPropertyInfo = type.GetProperty(_From);
        _ToPropertyInfo = type.GetProperty(_To);

        //gets the values of 2 dates from model (using reflection)
        DateTime? from = (DateTime?)_FromPropertyInfo.GetValue(value, null);
        DateTime? to = (DateTime?)_ToPropertyInfo.GetValue(value, null);

        //compare dates
        if ((from != null) && (to != null))
        {
            int result = from.Value.CompareTo(to.Value);

            switch (_Operation)
            {
                case Operations.LesserThan:
                    return result == -1;
                case Operations.LesserOrEquals:
                    return result <= 0;
                case Operations.Equals:
                    return result == 0;
                case Operations.NotEquals:
                    return result != 0;
                case Operations.GreaterOrEquals:
                    return result >= 0;
                case Operations.GreaterThan:
                    return result == 1;
            }
        }

        return true;
    }

    public override string FormatErrorMessage(string name)
    {
        DisplayNameAttribute aFrom = (DisplayNameAttribute)_FromPropertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
        DisplayNameAttribute aTo = (DisplayNameAttribute)_ToPropertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();

        return string.Format(ErrorMessageString,
            !string.IsNullOrWhiteSpace(aFrom.DisplayName) ? aFrom.DisplayName : _From,
            !string.IsNullOrWhiteSpace(aTo.DisplayName) ? aTo.DisplayName : _To);
    }
}

DateCompareAttributeAdapter.cs:

public class DateCompareAttributeAdapter : DataAnnotationsModelValidator<DateCompareAttribute> 
{
    public DateCompareAttributeAdapter(ModelMetadata metadata, ControllerContext context, DateCompareAttribute attribute)
        : base(metadata, context, attribute) {
    }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        if (!Attribute.IsValid(Metadata.Model))
        {
            yield return new ModelValidationResult
            {
                Message = ErrorMessage,
                MemberName = Attribute.MemberName
            };
        }
    }
}

Global.asax:

protected void Application_Start()
{
    // ...
    DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(DateCompareAttribute), typeof(DateCompareAttributeAdapter));
}

CustomViewModel.cs:

[DateCompare("StartDateTime", "EndDateTime", DateCompareAttribute.Operations.LesserOrEquals)]
public class CustomViewModel
{
    // Properties...

    public DateTime? StartDateTime
    {
        get;
        set;
    }

    public DateTime? EndDateTime
    {
        get;
        set;
    }
}
2 голосов
/ 24 ноября 2010

Мне не известен простой способ исправить это поведение.Это одна из причин, почему я ненавижу аннотации данных.Делать то же самое с FluentValidation было бы непросто:

public class ExampleViewModelValidator: AbstractValidator<ExampleViewModel>
{
    public ExampleViewModelValidator()
    {
        RuleFor(x => x.EndDate)
            .GreaterThan(x => x.StartDate)
            .WithMessage("end date must be after start date");
    }
}

FluentValidation имеет отличную поддержку и интеграцию с ASP.NET MVC .

0 голосов
/ 13 февраля 2014

При возврате результата проверки используйте конструктор с двумя параметрами. Передайте ему массив с context.MemberName в качестве единственного значения. Надеюсь, это поможет

<AttributeUsage(AttributeTargets.Property Or AttributeTargets.Field, AllowMultiple:=False)>


Public Class NonNegativeAttribute
Inherits ValidationAttribute
Public Sub New()


End Sub
Protected Overrides Function IsValid(num As Object, context As ValidationContext) As ValidationResult
    Dim t = num.GetType()
    If (t.IsValueType AndAlso Not t.IsAssignableFrom(GetType(String))) Then

        If ((num >= 0)) Then
            Return ValidationResult.Success
        End If
        Return New ValidationResult(context.MemberName & " must be a positive number",     New String() {context.MemberName})

    End If

    Throw New ValidationException(t.FullName + " is not a valid type. Must be a number")
End Function

End Class
0 голосов
/ 24 ноября 2010

Вам необходимо установить свойство ErrorMessage, например:

 public class DOBValidAttribute : ValidationAttribute
{
    private static string _errorMessage = "Date of birth is a required field.";

    public DOBValidAttribute() : base(_errorMessage)
    {

    }
//etc......overriding IsValid....
...