一个实战参考案例
思路:
1.封装同一类型的特性
2.每种情况封装特性
3.标注特性
4.手动调用
1.封装同一类型的特性
项目过程中,难免会用到数据检证,用特性的方式处理检证问题;抽象特性,抽象方法VirifiProperty,参数是属性的值,通过这个方法验证属性数据是否合格;
/// <summary>
/// 封装抽象父类,抽象字段验证函数
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]//标记特性能标记的范围(当前只能标记属性和字段)
public abstract class BasicAttribute : Attribute
{
protected string[] _ParaList;//接收测参数
/// <summary>
/// 构造函数接收参数
/// </summary>
/// <param name="para"></param>
public BasicAttribute(params string[] para)
{
_ParaList = para;
}
/// <summary>
/// 抽象字段验证方法
/// </summary>
/// <returns></returns>
public abstract void VirifiProperty(object value);
}
2.每种情况封装特性
对属性的长度,是否为空,是否为数值等,都封装成特性,这里是一个扩展点,可以根据不同的情况扩展多个特性,只要每个特性继承基类BasicAttribute
验证不合格可以有多种方式,当前项目用的是抛出自定义异常ExceptionAttribute,ExceptionAttribute是对Exception的封装,一是可以写一下特殊逻辑,一个是框架捕捉到异常后,可以区分出是特性抛出的异常以做单纯处理
/// <summary>
/// 字符串长度检验
/// </summary>
public class VirificationLenAttribute : BasicAttribute
{
/// <summary>
/// 构造函数,2个参数,第一个是长度,第二个是字段中文名称
/// </summary>
/// <param name="para"></param>
public VirificationLenAttribute(params string[] para) : base(para) { }
/// <summary>
/// 验证字段长度
/// </summary>
/// <param name="value">当前字段内容</param>
/// <returns></returns>
public override void VirifiProperty(object value)
{
if (value != null && value.ToString().Length > int.Parse(_ParaList[0]) && int.Parse(_ParaList[0]) != -1)
{
throw new ExceptionAttribute( $"列[{_ParaList[1]}]的长度超出范围,长度范围是{_ParaList[0]}");
}
}
}
/// <summary>
/// 字符串不允许为空
/// </summary>
public class VirificationNotNullAttribute : BasicAttribute
{
/// <summary>
/// 构造函数,1个参数,第一是字段中文名称
/// </summary>
/// <param name="para"></param>
public VirificationNotNullAttribute(params string[] para) : base(para) { }
/// <summary>
/// 验证是否为空
/// </summary>
/// <param name="value">当前字段内容</param>
/// <returns></returns>
public override void VirifiProperty(object value)
{
if (value == null || string.IsNullOrEmpty(value.ToString()))
{
throw new ExceptionAttribute($"列不允许为空");
}
}
}
/// <summary>
/// 整数值验证
/// </summary>
public class VirificationIntAttribute : BasicAttribute
{
/// <summary>
/// 构造函数,1个参数,字段中文名称
/// </summary>
/// <param name="para"></param>
public VirificationIntAttribute(params string[] para) : base(para) { }
/// <summary>
/// 判断是否是数值
/// </summary>
/// <param name="value">当前字段内容</param>
/// <returns></returns>
public override void VirifiProperty(object value)
{
try
{
//空的场合
if (value == null || string.IsNullOrEmpty(value.ToString()))
{
throw new ExceptionAttribute($"{_ParaList[0]}不能为空,请输入数值!");
}
int.Parse(value.ToString());
}
catch (Exception)
{
throw new ExceptionAttribute($"列[{_ParaList[0]}]是数值类型,请输入数值!");
}
}
}
public class ExceptionAttribute : Exception
{
public string msg { get; set; }
/// <summary>
///封装exception,后期框架中,可以区分是特性异常
/// </summary>
/// <param name="message">异常消息</param>
public ExceptionAttribute(string message)
: base(message)
{
msg = message;
}
}
3.标注特性
在属性上面可以标记多个特性
public class ModelDemo
{
[VirificationInt("序号")]
public int? id { get; set; }
[VirificationLen("4", "姓名")]
public string name { get; set; }
[VirificationNotNull]
[VirificationInt("序号")]
public int? age { get; set; }
public string remark { get; set; }
}
用扩展的方式,操作特性
/// <summary>
/// 类检证
/// </summary>
/// <param name="obj"></param>
public static void CheckModel( this object obj) {
//获取类所有属性
foreach (PropertyInfo item in obj.GetType().GetProperties())
{
//判断是否含有特性
if (item.IsDefined(typeof(BasicAttribute),true))
{
//找到当前类所有特性
var attributes = item.GetCustomAttributes(typeof(BasicAttribute), true);
foreach (var attribute in attributes)
{
//执行检证函数
BasicAttribute basicAttribute = (BasicAttribute)attribute;
basicAttribute.VirifiProperty(item.GetValue(obj));
}
}
}
}
4.手动调用
本次是用抛出异常的方式处理特殊不合格的情况,实际方式有很多,如果框架没有对异常单独处理,可以改成返回值的方式.
try
{
ModelDemo modelDemo = new ModelDemo();
modelDemo.id = 10;
modelDemo.name = "123";
modelDemo.age = null;
modelDemo.CheckModel();
}
catch (ExceptionAttribute ex)
{
//一般情况下,框架是用AOP的方式捕捉异常,集中处理
//为了方便用这种方式替代
Console.WriteLine(ex.msg);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();