无法在MVC2中设置自定义验证属性的成员名称

时间:2010-11-24 12:14:08

标签: asp.net-mvc validation asp.net-mvc-2 data-annotations

我通过继承ValidationAttribute创建了自定义验证属性。该属性在类级别应用于我的viewmodel,因为它需要验证多个属性。

我凌驾于

protected override ValidationResult IsValid(object value, ValidationContext validationContext)

并返回:

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

在DateOfBirth是我的视图模型的属性之一的所有情况下。

当我运行我的应用程序时,我可以看到它被击中。 ModelState.IsValid正确设置为false但是当我检查ModelState内容时,我看到Property DateOfBirth不包含任何错误。相反,我有一个值为null的空字符串Key和一个包含我在验证属性中指定的字符串的异常。

这会导致在使用ValidationMessageFor时,我的UI中不会显示任何错误消息。如果我使用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 个答案:

答案 0 :(得分:13)

大家好。

仍在寻找解决方案?

我今天解决了同样的问题。您必须创建自定义验证属性,该属性将验证2个日期(下面的示例)。然后你需要Adapter(验证器),它将使用你的自定义属性验证模型。最后一件事是使用属性绑定适配器。也许一些例子会比我更好地解释它:)

我们走了:

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

答案 1 :(得分:2)

我不知道修复此行为的简单方法。这就是我讨厌数据注释的原因之一。对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非常棒support and integration with ASP.NET MVC

答案 2 :(得分:0)

返回验证结果时,请使用两个参数构造函数。 将一个数组传递给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

答案 3 :(得分:-1)

您需要设置ErrorMessage属性,例如:

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

    public DOBValidAttribute() : base(_errorMessage)
    {

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