《ASP.NET MVC验证框架中关于属性标记的通用扩展方法》之继续扩展
首先需要对xVal有点熟悉:
http://www.codeplex.com/xval
建议下载最新源码而不是编译版本
再看两篇文章:
http://goneale.com/2009/03/04/using-metadatatype-attribute-with-aspnet-mvc-xval-validation-framework/
深山老林将之翻译为:《ASP.NET MVC验证框架中关于属性标记的通用扩展方法》
http://www.cnblogs.com/wlb/archive/2009/12/01/1614209.html
现在有个"比较验证"的需求,比如注册帐号时候,需要判断两次输入的密码是否一致,再比如有时候需要比较输入的值是否大于(小于)某个值或某个html控件的值等等。
而深山老林翻译提供的方法并不支持这种方式,原因是是什么呢?
进行比较时需要两个属性(或字段),ValidationAttribute如果用在某一个属性上,无法获取该属性所属的实例,也就无法获取另一个属性值进行比较。(如果可以获取还望一定告知)
因此,这里就用基于类的特性校验方式。
参考ASP.NET MVC 2 RC Demo 自定义类一个比较ValidationAttribute为StringCompareAttribute只是简单的比较连个字符串是否相等(完全有必要完善成类似于WebForm的CompareControl的效果):
1: [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
2: public class StringCompareAttribute : ValidationAttribute
3: {
4: public string SourceProperty { get; private set; }
5: public string OriginalProperty { get; private set; }
6:
7: private const string _defaultErrorMessage = "'{0}' 与 '{1}' 不相等";
8:
9: public StringCompareAttribute(string sourceProperty, string originalProperty)
10: : base(_defaultErrorMessage)
11: {
12: SourceProperty = sourceProperty;
13: OriginalProperty = originalProperty;
15: }
16:
17: public override string FormatErrorMessage(string name)
18: {
19: return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
20: SourceProperty, OriginalProperty);
21: }
22:
23: public override bool IsValid(object value)//value这里就不是属性或字段的值了,而是实体的实例
24: {
25: PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
26: object sourceProperty = properties.Find(SourceProperty, true /* ignoreCase */).GetValue(value);
27: object originalProperty = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
28:
29: return object.Equals(sourceProperty, originalProperty);
30: }
31:
32: }
33:
为了简单起见,实体类就不用伙伴类了:
1: [StringCompare("PasswordConfirm", "Password", ErrorMessage = "两次输入的密码不一致")]
2: public class User
3: {
4: [Required(ErrorMessage = "用户名不能为空")]
5: public string UserName { get; set; }
6:
7: [Required(ErrorMessage = "密码不能为空")]
8: public string Password { get; set; }
9:
10: [Required(ErrorMessage = "确认密码不能为空")]
11: public string PasswordConfirm { get; set; }
12: }
13:
现在直接用xVal的校验方式就不行了。
需要改成这样:
1: public static IEnumerable<ErrorInfo> GetErrors(this object instance)
2: {
3: //或返回一个只包含一个ErrorInfo对象的集合
4: if (instance == null) return null;
5:
6: //尝试获取类型的伙伴类型
7: var metadataAttrib = instance.GetType().GetCustomAttributes(typeof(MetadataTypeAttribute), true).OfType<MetadataTypeAttribute>().FirstOrDefault();
8: //获取具有校验数据的类型(伙伴类型或本类型)
9: var buddyClassOrModelClass = metadataAttrib != null ? metadataAttrib.MetadataClassType : instance.GetType();
10: //伙伴类型的属性
11: var buddyClassProperties = TypeDescriptor.GetProperties(buddyClassOrModelClass).Cast<PropertyDescriptor>();
12: //实际模型的属性
13: var modelClassProperties = TypeDescriptor.GetProperties(instance.GetType()).Cast<PropertyDescriptor>();
14:
15: //获取伙伴类型或本类型的校验特性
16: var buddyClassAttrib = TypeDescriptor.GetAttributes(buddyClassOrModelClass).OfType<ValidationAttribute>();
17: var modelClassAttrib = TypeDescriptor.GetAttributes(instance.GetType()).OfType<ValidationAttribute>();
18:
19: var errors = (
20: from buddyProp in buddyClassProperties
21: join modelProp in modelClassProperties on buddyProp.Name equals modelProp.Name
22: from attribute in buddyProp.Attributes.OfType<ValidationAttribute>()
23: where !attribute.IsValid(modelProp.GetValue(instance))
24: select new ErrorInfo(buddyProp.Name, attribute.FormatErrorMessage(string.Empty), instance)
25: ).Union(
26: from attribute in buddyClassAttrib
27: where !attribute.IsValid(instance)
28: select new ErrorInfo(string.Empty, attribute.FormatErrorMessage(string.Empty), instance)
29: ).Union(
30: from attribute in modelClassAttrib
31: where !attribute.IsValid(instance)
32: select new ErrorInfo(string.Empty, attribute.FormatErrorMessage(string.Empty), instance)
33: );
34:
35: return errors;
36: }
37: