`
tubaluer
  • 浏览: 1447254 次
文章分类
社区版块
存档分类
最新评论
  • sblig: c / c++ 是不一样的都会输出 100
    j = j++

用System.Attribute扩展元数据

 
阅读更多

本文以一个项目中通用的验证类来举例说明如何使用自定义Attribute来扩展元数据。

在项目中,我们为了保证各个层次之间的松藕合,通常把在各个层次之间传递数据的封装在一个称为实体类的类中,比如ActionFrom

using System;

namespace AttributeTest
{
public class ActionForm
{
private string email = "";
private string password = "";
public string Email
{
get{return this.email;}
set{this.email = value;}
}
public string Password
{
get{ return this.password;}
set{ this.password = value;}
}

}
}

现在,在使用这些实体类中的数据之前,我们需要对其中的数据进行验证。通常我们会写个静态类,用来提供各种不同的验证方法。比如需要验证Email,验证Password,比如:

using System;
using System.Reflection;
using System.Text.RegularExpressions;

namespace AttributeTest
{
public class Validator
{

public static bool ValidateEmail(string email) {...}

public static bool ValidatePassword(string passwd) {...}

}
}

这样的硬编码混迹于各个层次之间,一旦实体类里某个属性发生变化,就不得不修改各个层次中的相关验证代码。于是,我们想到可以使用一个统一的验证方法用来验证所有的实体类中的属性。

public static bool Validate(string propertyName, string propertyValue, Validator.ValidateType t) {...}

这里,Validator.ValidateType 是Validator中提供的一个枚举。

public enum ValidateType
{
Email,
Password,
Number,
Id
}

这里这个验证方法,的第三个参数使得验证与实体类的耦合密度增加了。我们还是不得不在修改实体类的时候,修改验证方法的调用代码。

现在,我们需要自定义Attribute来扩展实体类的元数据。通过对实体类元数据的描述,我们可以去掉验证方法里的第三个参数。

using System;

namespace AttributeTest
{

[System.AttributeUsage(AttributeTargets.Property)]
public class ValidateAttribute : System.Attribute
{
public ValidateAttribute(ValidateType validateType)
{
this.validateType = validateType;
}
private ValidateType validateType;

public ValidateType ValidateType
{
get{ return this.validateType; }
set{ this.validateType = value; }
}
}

public enum ValidateType
{
Email,
Password,
Number,
Id
}
}

自定义Attribute(特性)必须继承于System.Attribute。还可以通过System.AttributeUsageAttribute特性,控制自定义特性的使用范围(构件),例如,字段、方法。[System.AttributeUsage(AttributeTargets.Property)]限制这个自定义特性只能使用在类的属性上。

现在,我们实现这个验证方法:

using System;
using System.Reflection;
using System.Text.RegularExpressions;

namespace AttributeTest
{
public class Validator
{
public static bool Validate(object validateObject, string validateProperty)
{
System.Type t = validateObject.GetType();
PropertyInfo pi = t.GetProperty(validateProperty);

string validateValue = pi.GetValue(validateObject, null) as string;

if( pi.IsDefined(typeof(ValidateAttribute), true) )
{
object[] atts = pi.GetCustomAttributes(true);
ValidateAttribute vatt = atts[0] as ValidateAttribute;
string strExpr = "";
switch(vatt.ValidateType)
{
case ValidateType.Email:
strExpr = @"^[/w-]+(/.[/w-]+)*@[/w-]+(/.[/w-]+)+$";
break;
case ValidateType.Password:
strExpr = @"/d{6}";
break;
case ValidateType.Number:
strExpr = @"^/d*$";
break;
case ValidateType.Id:
strExpr = @"^/w*$";
break;
default:
return true;
}
Regex validateRegex = new Regex(strExpr);
return validateRegex.IsMatch(validateValue);
}
return true;
}
}
}
该方法需要两个参数,一个是需要验证的实体类的实例,还有一个是需要验证的属性名。当然,我们还需要在实体类上加上我们自定义的特性:

using System;

namespace AttributeTest
{
public class ActionForm
{
private string email = "";
private string password = "";

[Validate(ValidateType.Email)]
public string Email
{
get{return this.email;}
set{this.email = value;}
}

[Validate(ValidateType.Password)]
public string Password
{
get{return this.password;}
set {this.password = value;}
}

}
}

我们通过自定义特性对实体类的元数据进行扩展,指定每个属性需要验证的类型。

现在我们可以这样使用这个验证类:

ActionForm form = new ActionForm();
form.Email = justacoder@123.com;
form.Password = "123456";

bool isValidEmail = Validator.Validate(form, "Email");
bool isValidPassword = Validator.Validate(form, "Password");

Console.WriteLine("Email is {0}.", isValidEmail?"valid":"invalid");
Console.WriteLine("Password is {0}.", isValidPassword?"valid":"invalid");

Console.ReadLine();

我们通过抛出自定义异常的方法,将验证扩大到实体类级别的验证:

public static void ValidateProperty(object validateObject, string validateProperty)
{
System.Type t = validateObject.GetType();
PropertyInfo pi = t.GetProperty(validateProperty);

string validateValue = pi.GetValue(validateObject, null) as string;

if( pi.IsDefined(typeof(ValidateAttribute), true) )
{
object[] atts = pi.GetCustomAttributes(true);
ValidateAttribute vatt = atts[0] as ValidateAttribute;
string strExpr = "";
switch(vatt.ValidateType)
{
case ValidateType.Email:
strExpr = @"^[/w-]+(/.[/w-]+)*@[/w-]+(/.[/w-]+)+$";
break;
case ValidateType.Password:
strExpr = @"/d{6}";
break;
case ValidateType.Number:
strExpr = @"^/d*$";
break;
case ValidateType.Id:
strExpr = @"^/w*$";
break;
default:
return;
}
Regex validateRegex = new Regex(strExpr);
if( !validateRegex.IsMatch(validateValue) )
{
throw new ApplicationException(validateProperty + " is invalid.");
}
}
}

public static void Validate(object validateObject)
{
System.Type t = validateObject.GetType();
PropertyInfo[] ps = t.GetProperties();

foreach(PropertyInfo pi in ps)
{
ValidateProperty(validateObject, pi.Name);
}
}

现在验证,只需要这样:

try
{
Validator.Validate(form);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}

分享到:
评论

相关推荐

    C#5.0本质论第四版(因文件较大传的是百度网盘地址)

    17.1.1 使用System.Type访问元数据 472 17.1.2 成员调用 473 17.1.3 泛型类型上的反射 478 17.2 特性 479 17.2.1 自定义特性 482 17.2.2 查找特性 483 17.2.3 使用构造器来初始化特性 ...

    HibernateAPI中文版.chm

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    Hibernate中文详细学习文档

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    Hibernate 中文 html 帮助文档

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2. ...

    CLR.via.C#.(中文第3版)(自制详细书签)Part2

    2.3 元数据概述 2.4 将模块合并成程序集 2.4.1 使用Visual Studio IDE将程序集添加到项目中 2.4.2 使用程序集链接器 2.4.3 为程序集添加资源文件 2.5 程序集版本资源信息 2.5.1 版本号 2.6 语言文化 2.7 ...

    hibernate 体系结构与配置 参考文档(html)

    使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2. ...

    CLR.via.C#.(中文第3版)(自制详细书签)

    2.3 元数据概述 2.4 将模块合并成程序集 2.4.1 使用Visual Studio IDE将程序集添加到项目中 2.4.2 使用程序集链接器 2.4.3 为程序集添加资源文件 2.5 程序集版本资源信息 2.5.1 版本号 2.6 语言文化 2.7 ...

    Hibernate+中文文档

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    CLR.via.C#.(中文第3版)(自制详细书签)Part1

    2.3 元数据概述 2.4 将模块合并成程序集 2.4.1 使用Visual Studio IDE将程序集添加到项目中 2.4.2 使用程序集链接器 2.4.3 为程序集添加资源文件 2.5 程序集版本资源信息 2.5.1 版本号 2.6 语言文化 2.7 ...

    Hibernate_3.2.0_符合Java习惯的关系数据库持久化

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    CLR.via.C#.(中文第3版)(自制详细书签)Part3

    2.3 元数据概述 2.4 将模块合并成程序集 2.4.1 使用Visual Studio IDE将程序集添加到项目中 2.4.2 使用程序集链接器 2.4.3 为程序集添加资源文件 2.5 程序集版本资源信息 2.5.1 版本号 2.6 语言文化 2.7 ...

    hibernate3.2中文文档(chm格式)

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    ASP.NET的网页代码模型及生命周期

    在页面控件中,如果有多个自定义控件使用多个不同的控件来显示不同的数据结构,为了让这些页面控件能够在在页面上协调的工作,则需要使用控件状态来保护控件,同时,控件状态是不能被关闭的。同样,控件状态也有它的...

    xml入门教程/xml入门教程

    用记号标识非xml格式的数据 定义格式 <!NOTATION 记号名 SYSTEM "外部标识"> 3)DTD的缺陷,DTD中的类型比较少,有一些约束在DTD中是表达不了的,DTD不支持XML语法. &3.NameSpace(命名空间) namespace是...

    java 面试题 总结

    ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector...

    超级有影响力霸气的Java面试题大全文档

     ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,...

Global site tag (gtag.js) - Google Analytics