在MVC2.0 中 进行 LINQTOSQL 实体统一验证方法(上)

本文探讨了在MVC2.0框架中如何实现LINQ to SQL实体的数据验证,包括使用DataAnnotations进行简单验证及自定义验证特性以应对复杂验证需求的方法。

    场景

      当我把项目从 MVC1.0 升级到 MVC2.0 时,原以为可以方便的使用 System.ComponentModel.DataAnnotations 结合 MVC2.O 的

      ModelState.IsValid 进行数据有效验证。比如以下验证:


     
1 public class SystemUserMetaData
2 {
3 [Required(ErrorMessage = " 不能为空! " )]
4 [StringLength( 6 , ErrorMessage = " 用户名长度不能超过6! " )]
5 public string UserName { get; set; }
6 [Required(ErrorMessage = " IsValid is required. " )]
7 public string ChineseName { get; set; }
8 [Required(ErrorMessage = " IsValid is required. " )]
9 public bool IsValid { get; set; }
10 [Required(ErrorMessage = " Department is required. " )]
11 public int DepartmentID { get; set; }
12 [Required(ErrorMessage = " Password is required. " )]
13 public string Password { get; set; }
14 [Required(ErrorMessage = " Rank is required. " )]
15 public int RankID { get; set; }
16 [PhoneAttribute(ErrorMessage = " 电话号码不正确 " )]
17 public string MobilePhone { get; set; }
18 public int UserID { get; set; }
19 }
代码1

      
1 public class SystemUserMetaData
2 {
3 [Required(ErrorMessage = " 不能为空! " )]
4 [StringLength( 6 , ErrorMessage = " 用户名长度不能超过6! " )]
5 public string UserName { get ; set ; }
6 [Required(ErrorMessage = " 中文名不能为空 " )]
7 public string ChineseName { get ; set ; }
8 [Required(ErrorMessage = " 部门不能为空 " )]
9 public int DepartmentID { get ; set ; }
10 [Required(ErrorMessage = " 密码不能为空 " )]
11 public string Password { get ; set ; }
12 [Required(ErrorMessage = " 职位不能为空 " )]
13 public int RankID { get ; set ; }
14 [PhoneAttribute(ErrorMessage = " 电话号码不正确 " )] // 自定义ValidationAttribute “只能验证 MobilePhone 的值”
15   public string MobilePhone { get ; set ; }
16 public int UserID { get ; set ; }
17 }

     这些Annotation特性验证可以很轻松通过 mvc2.0  ViewData.ModelState.Values 获取到验证错误的提示信息。但是当我们的验证条件变得更加

复杂时,比如在修改一个LinqToSQL 实体时需通过该实体的主键和唯一索引进行验证实体是否唯一性时,此时需要两个字段同时验证,当这种验证出现时我

发现无法简单的使用 DataAnnotaion 进行同一实体的多字段验证。自定义 ValidationAttribute 特性重写 IsValid 时 无法根据当前的属性获取到其他属性

的值。因为ValidationAttribute 特性是附加在一个类的属性上的。可能聪明的你此刻已想到了将验证特性直接加载 LinqToSQL 的 类上。当你为这个特性

编写验证方法时就可以通过反射得到 LinqToSql 实体的所有属性的值,或许单一的 ValidationAttribute 属性验证特性不能完成的任务就可以得到解决。

        当我把LINQTOSQL 类的验证特性写完后附加到 LinqTOSQL partial 类上代码如下:


     
[UniqueName( " UserID " , " UserName " , typeof (SystemUser), ErrorMessage = " 该用户已存在。 " )]
[MetadataType(
typeof (SystemUserMetaData))]
public partial class SystemUser { }

在MVC2.0 中当我们使用 TryUpdateModel 方法时 发现 UniqueName 的 IsValid 方法始终没有被调用。但是当 MetadataType 移除除掉,我们再调用

TyUpdateaModel方法时UniqueName 特性的 IsValid 验证方法就被正常调用了。此时我明白了问题应该是由 MVC  TryUpdateModel 方法引起,将该方

法换成 UpdateModel 后问题依旧。MetadataType 特性覆盖了 UniqueName 特性,当然了如果想知道具体的原因,可以 Reflect 出 TryUpdateModel

的方法找到到答案。为了解决这个问题,我决定使用自定义的方法进行实体验证,代码如下:

代码3

      
public class Validation
{
public static void ValidateAttributes < TEntity > (TEntity entity)
{
var validationInstance
= new Validation();
validationInstance.ValidateAttributesInternal(entity);
}

public virtual void ValidateAttributesInternal < TEntity > (TEntity entity)
{
var validationIssues
= new List < ValidationIssue > ();

var props
= typeof (TEntity).GetProperties();
var metatype
= typeof (TEntity).GetCustomAttributes( typeof (MetadataTypeAttribute), false ).FirstOrDefault();
var type
= ((System.ComponentModel.DataAnnotations.MetadataTypeAttribute)(metatype)).MetadataClassType;
var s
= type.GetProperties();

var customAttrs
= typeof (TEntity).GetCustomAttributes( true ).Where(t => t.GetType().Namespace.Contains( " ValidationMeta " ));
foreach (var attr in customAttrs)
{
var validate
= (ValidationAttribute)attr;
// 执行 附加在 linqtosql partial 类 上的 ValidationAttribute 验证方法
bool valid = validate.IsValid(entity);
if ( ! valid)
{
validationIssues.Add(
new ValidationIssue( null , null , validate.ErrorMessage));
}
}

// 执行附加在 linqtosql partial 类 属性上的 ValidationAttribute 验证方法
foreach (var prop in s)
ValidateProperty(validationIssues, entity, prop);

// throw exception?
if (validationIssues.Count > 0 )
throw new ValidationIssueException(validationIssues);
}

protected virtual void ValidateProperty < TEntity > (List < ValidationIssue > validationIssues, TEntity entity, PropertyInfo property)
{
// 得到验证特性的集合
var validators = property.GetCustomAttributes( typeof (ValidationAttribute), false );

foreach (ValidationAttribute validator in validators)
ValidateValidator(validationIssues, entity, property, validator);
}

protected virtual void ValidateValidator < TEntity > (List < ValidationIssue > validationIssues, TEntity entity, PropertyInfo property, ValidationAttribute validator)
{
var dataEntityProperty
= typeof (TEntity).GetProperties().FirstOrDefault(p => p.Name == property.Name);
var value
= dataEntityProperty.GetValue(entity, null );

if ( ! validator.IsValid(value))
{
validationIssues.Add(
new ValidationIssue(property.Name, value, validator.ErrorMessage));
}
}
}

大家留意一下代码3 中的注释,这样 Validation 这个类就就可以替代MVC TryUpdateModel 的验证功能同时让代码1的 UniqueName 和 MetaDataType 两个特性 “共存”。

MetadataType 的职责:验证实体的单一属性值的有效性。

LINQ实体类上的其他的自定义特性:如代码1中的 UniqueName 则可以进行复杂的属性验证如多属性值同时验证等。

这样我们就彻底的解决了开发过程中验证代码统一的编码规范。而不是同一个数据有效性验证的代码满天飞的局面。

小结

当我完成了以上代码似乎已经达到了预期的目的,但测试代码时候发现如果使用TryUpdateModel 更新另外一个LINQTOSQL 模型(Order表),这个被

更新的模型从数据库上来看它属于 SystemUser 的外键表。通过Order表中的UserID 字段关联到 SystemUser。当Order实体被MVC TryUpdateModel 时会同时把SystemUser 的 自定义的 [UniqueName] 特性的方法 IsValid() 也调用了,很显然这不是我们想要的。该问题我会在下一篇文章提出解决方案。

posted on 2010-11-07 15:02 ryanding 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/ryanding/archive/2010/11/07/1869188.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值