开发项目过程中,导出都需要数据验证,接下来我们写一个实例,通过特性实现对我们项目中关于数据的验证,比如:对手机号、密码、邮箱等的非空、格式的验证行为等。
如果在没有特性支持的情况下,我们只能通过对每个字段if-else,正则等挨个进行验证,当然我们也可以抽象方法,可扩展性不好,对于架构而言是致命的。
为了更好的组织代码,我先创建了一个ValidateExtend文件夹来存放验证规则扩展特性。接下来直接上代码。
1 字段不能为空实现
1.1 创建RequireAttribute特性
[AttributeUsage(AttributeTargets.Property)]
public class RequireAttribute : Attribute
{
/// <summary>
/// 提示错误信息
/// </summary>
public string ErrorMessage { get; set; }
public RequireAttribute() { }
public override bool Validate(object oValue)
{
return oValue != null && oValue.ToString().Trim() != "";
}
}
1.2 创建扩展验证类AttributeExtend,用于调用验证,由于我创建了独立的验证规则ValidateExtend文件夹,因此不跟以前的命名空间冲突,是个全新的。此处注意我将类定为static静态类、静态方法以及参数中的this关键字,在调用时非常的方便!!!
public static class AttributeExtend
{
public static bool Validate<T>(this T t)
{
Type type = t.GetType();
foreach (var prop in type.GetProperties())
{
if (prop.IsDefined(typeof(RequireAttribute), true))
{
RequireAttribute ra = (RequireAttribute)prop.GetCustomAttribute(typeof(RequireAttribute), true);
return ra.Validate(prop.GetValue(t));
}
}
return true;
}
}
1.3 为属性新增特性,请注意Show防范中的调用方法,1.2中的写法就可以实现如下调用,当然,也可以在其他地方调用。
public class UserModel
{
[Require(ErrorMessage = "昵称不能为空")]
public string NickName { get; set; }
public void Show()
{
Console.WriteLine($"昵称:{this.NickName}");
// 执行规则验证,注意此处调用方法
Console.WriteLine($"规则验证是否通过:{this.Validate()}");
}
}
1.4 验证
class Program
{
static void Main(string[] args)
{
UserModel um = new UserModel()
{
// NickName = "十三点" //将昵称注释,不赋值
};
um.Show();
}
}
2 升级规则验证,新增多种规则验证
2.1 为了方便处理返回消息,先抽象出一个验证结果集ValidateResult类
/// <summary>
/// 验证规则结果集
/// </summary>
public class ValidateResult
{
/// <summary>
/// 是否通过验证
/// </summary>
public bool IsValidate = true;
/// <summary>
/// 返回错误消息列表
/// </summary>
public List<string> Messages = new List<string>();
}
2.2 考虑到将会有很多的特性处理,且调用时都需要调用不同的特性中的“验证”方法,因此,抽象出一个父级特性ValidateAttribute。
/// <summary>
/// 验证特性父级
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public abstract class ValidateAttribute : Attribute
{
public ValidateAttribute() { }
/// <summary>
/// 错误信息
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// 执行验证
/// </summary>
/// <param name="oValue">需要验证的特性字段</param>
/// <returns></returns>
public abstract bool Validate(object oValue);
}
2.3 修改原有不能为空特性验证类,继承ValidateAttribute。
/// <summary>
/// 为空验证约定
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class RequireAttribute : ValidateAttribute
{
public RequireAttribute() { }
public override bool Validate(object oValue)
{
return oValue != null && !string.IsNullOrEmpty(oValue.ToString().Trim());
}
}
2.4 新增字符串长度规则特性,Length,可用于判断密码长度等
/// <summary>
/// 验证字符长度是否符合设定长度
/// 默认6-20
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class LengthAttribute : ValidateAttribute
{
private int Min = 6;
private int Max = 20;
public LengthAttribute() { }
public LengthAttribute(int minLength, int maxLength)
{
this.Min = minLength;
this.Max = maxLength;
}
public override bool Validate(object oValue)
{
if (oValue == null) return false;
return Validate(oValue.ToString());
}
public bool Validate(string oValue)
{
return oValue.Length >= Min && oValue.Length <= Max;
}
}
2.5 改进AttributeExtend扩展类
public static class AttributeExtend
{
public static ValidateResult Validate<T>(this T t)
{
ValidateResult res = new ValidateResult();
Type type = t.GetType();
foreach (var prop in type.GetProperties())
{
if (prop.IsDefined(typeof(ValidateAttribute), true))
{
// 此处需要验证所有使用了验证规则特性的属性
foreach (var item in prop.GetCustomAttributes(typeof(ValidateAttribute), true))
{
ValidateAttribute validateAttribute = (ValidateAttribute)item;
if (!validateAttribute.Validate(prop.GetValue(t)))
{
res.IsValidate = false;
res.Messages.Add(
string.IsNullOrEmpty(validateAttribute.ErrorMessage) ?
$"请检查{prop.Name}参数赋值情况" : validateAttribute.ErrorMessage
);
// 一个属性多个规则条件下,每个属性只判断一次规则
break;
}
}
}
}
return res;
}
}
2.6 在实体中使用验证特性
public class UserModel : ModelBase
{
public int Id { get; set; }
[Require(ErrorMessage = "昵称不能为空")]
public string NickName { get; set; }
/// <summary>
/// 用户状态
/// </summary>
public UserState UserState { get; set; }
/// <summary>
/// 密码
/// </summary>
[Require(ErrorMessage = "密码不能为空")]
[Length(6, 12, ErrorMessage = "密码长度必须为6-12个字")]
public string Password { get; set; }
[Long(10000,999999999999,ErrorMessage = "QQ长度不符")]
public long QQ { get; set; }
public void Show()
{
Console.WriteLine($"ID:{this.Id} - {this.Id}");
Console.WriteLine($"昵称:{this.NickName}");
Console.WriteLine($"QQ:{this.QQ}");
ValidateResult vr = this.Validate();
Console.WriteLine(string.Format("规则验证:{0}", vr.IsValidate ? "通过" : "不通过"));
foreach (var item in vr.Messages)
{
Console.WriteLine($"{item}");
}
}
}
2.7 验证
class Program
{
static void Main(string[] args)
{
UserModel um = new UserModel() { Id = 1, Password ="123", QQ = 1111 };
um.Show();
}
}
OK,欢迎吐槽~
特性系列:
.Net —— 深入理解”特性”(1)源码解读 https://my.oschina.net/berzhuan/blog/4882044
.Net —— 深入理解”特性”(2)手撸特性提供额外属性,如:状态描述、对象与数据库及字段绑定等 https://my.oschina.net/berzhuan/blog/4890289
.Net —— 深入理解”特性”(3)手撸特性提供额外行为,如:字段数据验证 https://my.oschina.net/berzhuan/blog/4890290
{{m.name}}