代码之家  ›  专栏  ›  技术社区  ›  Darin Dimitrov

使用数据注释自定义依赖属性的模型验证

  •  43
  • Darin Dimitrov  · 技术社区  · 15 年前

    从现在起我就用了 FluentValidation 用于验证模型类的库。在Web应用程序中,我将它与 jquery.validate 用于执行客户端验证的插件。 一个缺点是,许多验证逻辑在客户端重复,不再集中在一个地方。

    因为这个原因,我正在寻找另一种选择。有 many 举例说明 there 显示使用数据注释执行模型验证。看起来很有前途。 我找不到的一件事是如何验证依赖于另一个属性值的属性。

    以下面的模型为例:

    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 already supports ASP.NET MVC 2中的客户端验证和元数据。

    不过,最好还是知道数据注释是否可以用于验证依赖属性。

    5 回复  |  直到 9 年前
        1
  •  29
  •   Travis Illig    14 年前

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

    Brad Wilson has a good example on his blog

        2
  •  14
  •   Nick Riggs    14 年前

    http://foolproof.codeplex.com/

    public class Event
    {
        [Required]
        public DateTime? StartDate { get; set; }
    
        [Required]
        [GreaterThan("StartDate")]
        public DateTime? EndDate { get; set; }
    }
    
        3
  •  7
  •   orcy    13 年前

    http://devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-1

    public class RegisterModel
    {
        // skipped
    
        [Required]
        [ValidatePasswordLength]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }                       
    
        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation do not match.")]
        public string ConfirmPassword { get; set; }
    }
    

        4
  •  4
  •   jwaliszko    9 年前

    [Required]
    public DateTime? StartDate { get; set; }    
    [Required]
    [AssertThat("StartDate != null && EndDate > StartDate")]
    public DateTime? EndDate { get; set; }
    

    can be found here

        5
  •  3
  •   Steven    15 年前

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

    public class CustomValidationAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
             Event e = (Event)ObjectContext.CurrentContext;
    
             // validate event here.
        }
    }
    

    Event eventToValidate = [....];
    using (var scope new ContextScope(eventToValidate))
    {
        DataAnnotations.Validator.Validate(eventToValidate);
    }