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

Pau*_*les 9 validation asp.net-mvc data-annotations asp.net-mvc-2

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

我压倒一切

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
Run Code Online (Sandbox Code Playgroud)

并返回:

new ValidationResult("Always Fail", new List<string> { "DateOfBirth" }); 
Run Code Online (Sandbox Code Playgroud)

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

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

这导致在使用ValidationMessageFor时,我的UI中不显示任何错误消息.如果我使用ValidationSummary,那么我可以看到错误.这是因为它与属性无关.

它看起来好像忽略了我在验证结果中指定了membername的事实.

为什么这样,我该如何解决?

要求的示例代码:

 [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; }
    }
Run Code Online (Sandbox Code Playgroud)

小智 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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
            };
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Global.asax中:

protected void Application_Start()
{
    // ...
    DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(DateCompareAttribute), typeof(DateCompareAttributeAdapter));
}
Run Code Online (Sandbox Code Playgroud)

CustomViewModel.cs:

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

    public DateTime? StartDateTime
    {
        get;
        set;
    }

    public DateTime? EndDateTime
    {
        get;
        set;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 说真的,这就是解决方案. (2认同)

Dar*_*rov 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

FluentValidation对 ASP.NET MVC 有很好的支持和集成

  • 谢谢。那么 MVC 团队正在重用 ValidationResult 类,但完全忽略其中一个属性?总的来说,MVC 团队的成果给我留下了深刻的印象,但这非常糟糕。我刚刚在MVC3/.NET4里查了一下,还是一样。 (3认同)