MVC2模型验证互斥所需的注释

时间:2010-09-09 12:09:33

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

是否有人知道使用ModelValidator互相排他地检查两个属性的好算法?

类似的东西:

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

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

[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并使用属性属性和反射来使其更通用。