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进行简单的相等(==)校验,但仅此而已,其他校验就没有了。
如果果要Model中有两个时间,开始时间和结束时间,要求结束时间必须大于开始时间:
ASP.NET MVC 3也有一个CompareAttribute,但它只能进行Equals比较。同样的比较两次密码是否一致,以上代码可以写成这样:
代码
这里我们定义一个类似WebForms验证服务器控件CompareValidator的Attribule。
首先定义一个枚举表示我们要比较的数据类型:
public enum ValidationDataType : byte
{
String,Integer,Double,Date,Currency/*Decimal*/
}
{
String,Integer,Double,Date,Currency/*Decimal*/
}
再定义一个枚举表示比较方式:
public enum ValidationCompareOperator : byte
{
Equal, NotEqual,GreaterThan,GreaterThanEqual,LessThan,LessThanEqual
}
{
Equal, NotEqual,GreaterThan,GreaterThanEqual,LessThan,LessThanEqual
}
然后当然就是主角CompareAttribute:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class CompareAttribute : ValidationAttribute
{
public string SourceProperty { get; private set; }
public string OriginalProperty { get; private set; }
public ValidationCompareOperator Operator { get; private set; }
public ValidationDataType Type{ get; private 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;
}
}
public class CompareAttribute : ValidationAttribute
{
public string SourceProperty { get; private set; }
public string OriginalProperty { get; private set; }
public ValidationCompareOperator Operator { get; private set; }
public ValidationDataType Type{ get; private 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; }
}
,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 { get; set; }
[DisplayName("结束时间")]
public DateTime TimeEnd { get; set; }
}
, ValidationDataType.Date, ErrorMessage = "结束时间必须大于开始时间")]
public class UserInputEdit
{
[DisplayName("开始时间")]
public DateTime TimeStart { get; set; }
[DisplayName("结束时间")]
public DateTime TimeEnd { get; set; }
}
ASP.NET MVC 3也有一个CompareAttribute,但它只能进行Equals比较。同样的比较两次密码是否一致,以上代码可以写成这样:
public class UserInputEdit
{
[DisplayName("登录密码")]
public string Password { get; set; }
[DisplayName("确认密码")]
[Compare("Password", ErrorMessage = "请确认两次输入的密码一致")]
public string PasswordConfirm { get; set; }
}
{
[DisplayName("登录密码")]
public string Password { get; set; }
[DisplayName("确认密码")]
[Compare("Password", ErrorMessage = "请确认两次输入的密码一致")]
public string PasswordConfirm { get; set; }
}
在我们自定义的CompareAttribute中,必须将Attribute应用于class上。如果您想像ASP.NET MVC 3中,将标记写在属性,很遗憾不行。因为ValidationResult IsValid(...)的方法在.Net 4才有,MVC 3也才有相应的支持。如果您已经在使用.Net 4,也可以像下面一样修改我们自定义的CompareAttribute,但请注意以下代码我没测试过:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field
, AllowMultiple = true)]
public class CompareAttribute : ValidationAttribute
{
public string OriginalProperty { get; private set; }
public ValidationCompareOperator Operator { get; private set; }
public ValidationDataType Type{ get; private 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;
}
}
, AllowMultiple = true)]
public class CompareAttribute : ValidationAttribute
{
public string OriginalProperty { get; private set; }
public ValidationCompareOperator Operator { get; private set; }
public ValidationDataType Type{ get; private 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;
}
}