ASP.Net MVC探索之路 - Model的比较验证

WebForm有一个比较验证服务器控件CompareValidator ,可以比较两个控件的值大小或一个控件与某一个具体值的大小。可以进行string,int,double,DateTime,decimal这些数据类型的==、!=、>、<、>=、<=比较。在ASP.NET MVC 2.0中,我们已经可以使用基于DataAnnotations的校验方式,对Model的值进行空校验(Required)、范围校验(Range)、字符串长度校验(StringLengthAttribute)等。在ASP.NET MVC 3中,我们还可以使用CompareAttribute进行简单的相等(==)校验,但仅此而已,其他校验就没有了。


这里我们定义一个类似WebForms验证服务器控件CompareValidator的Attribule。

首先定义一个枚举表示我们要比较的数据类型:

    public enum ValidationDataType : byte
    {
        String,Integer,Double,Date,Currency
/*Decimal*/
    }


再定义一个枚举表示比较方式:

    public enum ValidationCompareOperator : byte
    {
        Equal, NotEqual,GreaterThan,GreaterThanEqual,LessThan,LessThanEqual
    }


然后当然就是主角CompareAttribute:

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    
public class CompareAttribute : ValidationAttribute
    {
        
public string SourceProperty { getprivate set; }
        
public string OriginalProperty { getprivate set; }
        
public ValidationCompareOperator Operator { getprivate set; }
        
public ValidationDataType Type{ getprivate set; }

        
private const string _defaultErrorMessage = "'{0}' 与 '{1}' 进行 {2} 比较失败";

        
public CompareAttribute(string sourceProperty, string originalProperty
, ValidationCompareOperator op, ValidationDataType type)
            : 
base(_defaultErrorMessage)
        {
            SourceProperty 
= sourceProperty;
            OriginalProperty 
= originalProperty;
            Operator 
= op;
            Type 
= type;
        }

        
public override string FormatErrorMessage(string name)
        {
            
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                SourceProperty, OriginalProperty, Operator.ToString());
        }

        
public override bool IsValid(object value)
        {
            PropertyDescriptorCollection properties 
= TypeDescriptor.GetProperties(value);
            
object sourceProperty = properties.Find(SourceProperty, true /* ignoreCase */)
.GetValue(value);
            
object originalProperty = properties.Find(OriginalProperty, true /* ignoreCase */)
.GetValue(value);

            
return compare(sourceProperty,originalProperty);
        }

        
private bool compare(object sourceProperty, object originalProperty)
        {
            
int num = 0;
            
switch (this.Type)
            { 
                
case ValidationDataType.String:
                    num 
= string.Compare((string)sourceProperty
, (
string)originalProperty, false, CultureInfo.CurrentCulture);
                    
break;

                
case ValidationDataType.Integer:
                    num 
= ((int)sourceProperty).CompareTo(originalProperty);
                    
break;

                
case ValidationDataType.Double:
                    num 
= ((double)sourceProperty).CompareTo(originalProperty);
                    
break;

                
case ValidationDataType.Date:
                    num 
= ((DateTime)sourceProperty).CompareTo(originalProperty);
                    
break;

                
case ValidationDataType.Currency:
                    num 
= ((decimal)sourceProperty).CompareTo(originalProperty);
                    
break;
            }
            
switch (this.Operator)
            {
                
case ValidationCompareOperator.Equal:
                    
return (num == 0);

                
case ValidationCompareOperator.NotEqual:
                    
return (num != 0);

                
case ValidationCompareOperator.GreaterThan:
                    
return (num > 0);

                
case ValidationCompareOperator.GreaterThanEqual:
                    
return (num >= 0);

                
case ValidationCompareOperator.LessThan:
                    
return (num < 0);

                
case ValidationCompareOperator.LessThanEqual:
                    
return (num <= 0);
            }
            
return true;

        }
    }


在Model中就可以这样使用了,以下校验两次输入的密码是否一致:

    [Compare("PasswordConfirm""Password", ValidationCompareOperator.Equal
,ValidationDataType.String, ErrorMessage 
= "请确认两次输入的密码一致")]
    
public class UserInputEdit
    {
        [DisplayName(
"登录密码")]
        
public string Password { get; set; }

        [DisplayName(
"确认密码")]
        
public string PasswordConfirm { get; set; }
    }

如果果要Model中有两个时间,开始时间和结束时间,要求结束时间必须大于开始时间:
    [Compare("TimeEnd""TimeStart", ValidationCompareOperator.GreaterThan
, ValidationDataType.Date, ErrorMessage 
= "结束时间必须大于开始时间")]
    
public class UserInputEdit
    {
        [DisplayName(
"开始时间")]
        
public DateTime TimeStart { getset; }

        [DisplayName(
"结束时间")]
        
public DateTime TimeEnd { getset; }
    }

ASP.NET MVC 3也有一个CompareAttribute,但它只能进行Equals比较。同样的比较两次密码是否一致,以上代码可以写成这样:
    public class UserInputEdit
    {
        [DisplayName(
"登录密码")]
        
public string Password { getset; }

        [DisplayName(
"确认密码")]
        [Compare(
"Password", ErrorMessage = "请确认两次输入的密码一致")]
        
public string PasswordConfirm { getset; }
    }


在我们自定义的CompareAttribute中,必须将Attribute应用于class上。如果您想像ASP.NET MVC 3中,将标记写在属性,很遗憾不行。因为ValidationResult IsValid(...)的方法在.Net 4才有,MVC 3也才有相应的支持。如果您已经在使用.Net 4,也可以像下面一样修改我们自定义的CompareAttribute,但请注意以下代码我没测试过:

代码
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field
, AllowMultiple 
= true)]
    
public class CompareAttribute : ValidationAttribute
    {
        
public string OriginalProperty { getprivate set; }
        
public ValidationCompareOperator Operator { getprivate set; }
        
public ValidationDataType Type{ getprivate set; }

        
private const string _defaultErrorMessage = "'{0}' 与 '{1}' 进行 {2} 比较失败";

        
public CompareAttribute(string originalProperty
, ValidationCompareOperator op, ValidationDataType type)
            : 
base(_defaultErrorMessage)
        {
            OriginalProperty 
= originalProperty;
            Operator 
= op;
            Type 
= type;
        }

        
public override string FormatErrorMessage(string name)
        {
            
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                name, OriginalProperty, Operator.ToString());
        }

        
protected override ValidationResult IsValid(object value
, ValidationContext validationContext)
        {
            PropertyInfo originalProperty 
= validationContext.ObjectType
.GetProperty(
this.OriginalProperty);
            
object originalValue = originalProperty.GetValue(validationContext.ObjectInstance, null);

            
if(!compare(value, originalValue))
                
return new ValidationResult(this.FormatErrorMessage(validationContext
.DisplayName));

            
return null;
        }

        
private bool compare(object sourceProperty, object originalProperty)
        {
            
int num = 0;
            
switch (this.Type)
            { 
                
case ValidationDataType.String:
                    num 
= string.Compare((string)sourceProperty
, (
string)originalProperty, false, CultureInfo.CurrentCulture);
                    
break;

                
case ValidationDataType.Integer:
                    num 
= ((int)sourceProperty).CompareTo(originalProperty);
                    
break;

                
case ValidationDataType.Double:
                    num 
= ((double)sourceProperty).CompareTo(originalProperty);
                    
break;

                
case ValidationDataType.Date:
                    num 
= ((DateTime)sourceProperty).CompareTo(originalProperty);
                    
break;

                
case ValidationDataType.Currency:
                    num 
= ((decimal)sourceProperty).CompareTo(originalProperty);
                    
break;
            }
            
switch (this.Operator)
            {
                
case ValidationCompareOperator.Equal:
                    
return (num == 0);

                
case ValidationCompareOperator.NotEqual:
                    
return (num != 0);

                
case ValidationCompareOperator.GreaterThan:
                    
return (num > 0);

                
case ValidationCompareOperator.GreaterThanEqual:
                    
return (num >= 0);

                
case ValidationCompareOperator.LessThan:
                    
return (num < 0);

                
case ValidationCompareOperator.LessThanEqual:
                    
return (num <= 0);
            }
            
return true;

        }
    }


posted @ 2010-12-10 14:00  alby  阅读(2992)  评论(2编辑  收藏  举报