【问题标题】:Unable to set membernames from custom validation attribute in MVC2无法从 MVC2 中的自定义验证属性设置成员名
【发布时间】:2011-05-15 01:50:31
【问题描述】:

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

我正在覆盖

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

【问题讨论】:

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


    【解决方案1】:

    需要设置ErrorMessage属性,例如:

     public class DOBValidAttribute : ValidationAttribute
    {
        private static string _errorMessage = "Date of birth is a required field.";
    
        public DOBValidAttribute() : base(_errorMessage)
        {
    
        }
    //etc......overriding IsValid....
    

    【讨论】:

    • 错误消息正在设置中,并且如上所述。问题在于成员名应该映射到 ModelState 中的键。
    • 那你能把你的代码贴出来,这样我们可以更容易地提供帮助
    【解决方案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 很棒。

    【讨论】:

    • 谢谢。所以 MVC 团队正在重用 ValidationResult 类,但完全忽略了其中一个属性?总的来说,MVC 团队的输出给我留下了深刻的印象,但这很糟糕。我刚刚在 MVC3/.NET4 中检查过,还是一样。
    【解决方案3】:

    大家好。

    还在寻找解决方案?

    我今天解决了同样的问题。您必须创建将验证 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
                };
            }
        }
    }
    

    全球.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;
        }
    }
    

    【讨论】:

    • 干得好。在 MVC3 中,您可以使用 validationContext.ObjectInstance 属性从属性级别属性访问其他属性,这对我来说效果很好。你可以在这里看到一个例子:favcode.net/browse/…
    • 说真的,这就是解决方案。
    • 有什么方法可以让这个验证在客户端运行?
    【解决方案4】:

    返回验证结果时使用两个参数的构造函数。 将 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-14
      • 2021-04-09
      • 1970-01-01
      • 2019-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多