用System.Attribute扩展元数据

使用自定义Attribute扩展元数据进行验证
本文以项目通用验证类为例,介绍使用自定义Attribute扩展元数据的方法。先阐述传统验证方法硬编码及耦合问题,接着说明自定义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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值