【问题标题】:MVC2 model validation mutually exclusive required annotationMVC2模型验证互斥所需注解
【发布时间】:2010-09-09 12:09:33
【问题描述】:

有人知道使用ModelValidator 互斥地检查两个属性的好算法吗?

类似:

    [EitherPropertyRequired("BuildingNumber","BuildingName"]
    public class Address{
       public int BuildingNumber { get; set; }
       public string BuildingName { get; set; }
   }

【问题讨论】:

    标签: c# asp.net asp.net-mvc validation


    【解决方案1】:

    我最终创建了一个属性并使用自定义ModelValidator 手动检查它。使用在Application_Start() 中注册的AssociatedValidatorProvider 检查此自定义模型验证器。

    protected void Application_Start()
    {
        ModelValidatorProviders.Providers.Add(new ZipValidationProvider());
    }
    
    public class ZipValidationProvider:AssociatedValidatorProvider
    {
        protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
        {
            foreach (var attribute in attributes.OfType<EitherPropertyRequiredAttribute>())
            {
                yield return new EitherPropertyRequiredValidator(metadata,
                    context, attribute.FirstProperty, attribute.SecondProperty, attribute.Message);
            }
        }
    }
    
    [AttributeUsage(AttributeTargets.Class)]
    public class EitherPropertyRequiredAttribute : Attribute
    {
        public readonly string FirstProperty;
        public readonly string SecondProperty;
        public readonly string Message;
    
        public EitherPropertyRequiredAttribute(string firstProperty, string secondProperty, 
            string message)
        {
            FirstProperty = firstProperty;
            SecondProperty = secondProperty;
            Message = message;
        }
    }
    
    public class EitherPropertyRequiredValidator:ModelValidator
    {
        private readonly string firstProperty;
        private readonly string secondProperty;
        private readonly string message;
    
        public EitherPropertyRequiredValidator(ModelMetadata metadata,
                                        ControllerContext context,
            string firstProperty, 
            string secondProperty,
            string message)
            :base(metadata,context)
        {
            this.firstProperty = firstProperty;
            this.secondProperty = secondProperty;
            this.message = message;
        }
    
        private PropertyInfo GetPropertyInfoRecursive(Type type, string property)
        {
            var prop = type.GetProperty(property);
            if (prop != null) return prop;
    
            foreach (var p in type.GetProperties())
            {
                if (p.PropertyType.Assembly == typeof (object).Assembly)
                    continue;
    
                return GetPropertyInfoRecursive(p.PropertyType, property);
            }
    
            return null;
        }
    
        private object GetPropertyValueRecursive(object obj, PropertyInfo propertyInfo)
        {
            Type objectType = obj.GetType();
    
           if(objectType.GetProperty(propertyInfo.Name) != null)
                return propertyInfo.GetValue(obj, null);
    
            foreach (var p in objectType.GetProperties())
            {
                if (p.PropertyType.Assembly == typeof(object).Assembly)
                    continue;
    
                var o = p.GetValue(obj,null);
                return GetPropertyValueRecursive(o, propertyInfo);
            }
    
            return null;
        }
    
        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            if (Metadata.Model == null)
                yield break;
    
            var firstPropertyInfo = GetPropertyInfoRecursive(Metadata.Model.GetType(),firstProperty);
            if(firstPropertyInfo == null)
                throw new InvalidOperationException("Unknown property:" + firstProperty);
    
            var secondPropertyInfo = GetPropertyInfoRecursive(Metadata.Model.GetType(),secondProperty);
            if(secondPropertyInfo == null)
                throw new InvalidOperationException("Unknown property:" + secondProperty);
    
            var firstPropertyValue = GetPropertyValueRecursive(Metadata.Model, firstPropertyInfo);
            var secondPropertyValue = GetPropertyValueRecursive(Metadata.Model, secondPropertyInfo);
    
            bool firstPropertyIsEmpty = firstPropertyValue == null ||
                                        firstPropertyValue.ToString().Length == 0;
    
            bool secondPropertyIsEmpty = secondPropertyValue == null ||
                                         secondPropertyValue.ToString().Length == 0;
    
            if (firstPropertyIsEmpty && secondPropertyIsEmpty)
            {
                    yield return new ModelValidationResult
                    {
                        MemberName = firstProperty,
                        Message = message
                    };
            }
        }
    }
    

    【讨论】:

    • David,我已经实现了几个与您在上面发布的类似的验证器。我遇到的一个问题是在进行客户端输入验证时 ViewModels 中的嵌套类。
    • 您的意思是,如果您在表示嵌套类的实际对象上使用了上述验证属性,还是在遍历视图模型的属性时使用了上述验证属性?
    【解决方案2】:
    [AttributeUsage(AttributeTargets.Class)]
    public class EitherPropertyRequiredAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            // value will be the model
    
            Address address = (Address)value;
    
            // TODO: Check the properties of address here and return true or false
    
            return true;
        }
    }
    

    您可以通过避免将其转换为 Address 并使用属性属性和反射来使其更通用。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多