《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:   
posted @ 2010-01-07 19:00  alby  阅读(2125)  评论(4编辑  收藏  举报