Attribute(特性)的概念不在此赘述了,相信有点.NET基础的开发人员都明白,用过Attribute的人也不在少数,毕竟很多框架都提供自定义的属性,类似于Newtonsoft.JSON中JsonProperty、JsonIgnore等

自定义特性

.NET 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。

创建并使用自定义特性包含四个步骤:

  • 声明自定义特性
  • 构建自定义特性
  • 在目标程序元素上应用自定义特性
  • 通过反射访问特性

声明自定义特性

一个新的自定义特性必须派生自System.Attribute类,例如:

public class FieldDescriptionAttribute : Attribute
{
public string Description { get; private set; } public FieldDescriptionAttribute(string description)
{
Description = description;
}
}
public class UserEntity
{
[FieldDescription("用户名称")]
public string Name { get; set; }
}

该如何拿到我们标注的信息呢?这时候需要使用反射获取

      var type = typeof(UserEntity);
var properties = type.GetProperties();
foreach (var item in properties)
{
if(item.IsDefined(typeof(FieldDescriptionAttribute), true))
{
var attribute = item.GetCustomAttribute(typeof(FieldDescriptionAttribute)) as FieldDescriptionAttribute;
Console.WriteLine(attribute.Description);
}
}

执行结果如下:

从执行结果上看,我们拿到了我们想要的数据,那么这个特性在实际使用过程中,到底有什么用途呢?

Attribute特性妙用

在实际开发过程中,我们的系统总会提供各种各样的对外接口,其中参数的校验是必不可少的一个环节。然而没有特性时,校验的代码是这样的:

  public class UserEntity
{
/// <summary>
/// 姓名
/// </summary>
[FieldDescription("用户名称")]
public string Name { get; set; } /// <summary>
/// 年龄
/// </summary>
public int Age { get; set; } /// <summary>
/// 地址
/// </summary>
public string Address { get; set; }
}
      UserEntity user = new UserEntity();

      if (string.IsNullOrWhiteSpace(user.Name))
{
throw new Exception("姓名不能为空");
}
if (user.Age <= 0)
{
throw new Exception("年龄不合法");
}
if (string.IsNullOrWhiteSpace(user.Address))
{
throw new Exception("地址不能为空");
}

字段多了之后这种代码就看着非常繁琐,并且看上去不直观。对于这种繁琐又恶心的代码,有什么方法可以优化呢?

使用特性后的验证写法如下:

首先定义一个基础的校验属性,提供基础的校验方法

    public abstract class AbstractCustomAttribute : Attribute
{
/// <summary>
/// 校验后的错误信息
/// </summary>
public string ErrorMessage { get; set; } /// <summary>
/// 数据校验
/// </summary>
/// <param name="value"></param>
public abstract void Validate(object value);
}

然后可以定义常用的一些对应的校验Attribute,例如RequiredAttribute、StringLengthAttribute

        /// <summary>
/// 非空校验
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class RequiredAttribute : AbstractCustomAttribute
{
public override void Validate(object value)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
throw new Exception(string.IsNullOrWhiteSpace(ErrorMessage) ? "字段不能为空" : ErrorMessage);
}
}
} /// <summary>
/// 自定义验证,验证字符长度
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class StringLengthAttribute : AbstractCustomAttribute
{
private int _maxLength;
private int _minLength; public StringLengthAttribute(int minLength, int maxLength)
{
this._maxLength = maxLength;
this._minLength = minLength;
} public override void Validate(object value)
{
if (value != null && value.ToString().Length >= _minLength && value.ToString().Length <= _maxLength)
{
return;
} throw new Exception(string.IsNullOrWhiteSpace(ErrorMessage) ? $"字段长度必须在{_minLength}与{_maxLength}之间" : ErrorMessage);
}
}

添加一个用于校验的ValidateExtensions

public static class ValidateExtensions
{
/// <summary>
/// 校验
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static void Validate<T>(this T entity) where T : class
{
Type type = entity.GetType(); foreach (var item in type.GetProperties())
{
//需要对Property的字段类型做区分处理针对Object List 数组需要做区分处理
if (item.PropertyType.IsPrimitive || item.PropertyType.IsEnum || item.PropertyType.IsValueType || item.PropertyType == typeof(string))
{
//如果是基元类型、枚举类型、值类型或者字符串 直接进行校验
CheckProperty(entity, item);
}
else
{
//如果是引用类型
var value = item.GetValue(entity, null);
CheckProperty(entity, item);
if (value != null)
{
if ((item.PropertyType.IsGenericType && Array.Exists(item.PropertyType.GetInterfaces(), t => t.GetGenericTypeDefinition() == typeof(IList<>))) || item.PropertyType.IsArray)
{
//判断IEnumerable
var enumeratorMI = item.PropertyType.GetMethod("GetEnumerator");
var enumerator = enumeratorMI.Invoke(value, null);
var moveNextMI = enumerator.GetType().GetMethod("MoveNext");
var currentMI = enumerator.GetType().GetProperty("Current");
int index = 0;
while (Convert.ToBoolean(moveNextMI.Invoke(enumerator, null)))
{
var currentElement = currentMI.GetValue(enumerator, null);
if (currentElement != null)
{
currentElement.Validate();
}
index++;
}
}
else
{
value.Validate();
}
}
}
}
} private static void CheckProperty(object entity, PropertyInfo property)
{
if (property.IsDefined(typeof(AbstractCustomAttribute), true))//此处是重点
{
//此处是重点
foreach (AbstractCustomAttribute attribute in property.GetCustomAttributes(typeof(AbstractCustomAttribute), true))
{
if (attribute == null)
{
throw new Exception("AbstractCustomAttribute not instantiate");
} attribute.Validate(property.GetValue(entity, null));
}
}
}
}

新的实体类

  public class UserEntity
{
/// <summary>
/// 姓名
/// </summary>
[Required]
public string Name { get; set; } /// <summary>
/// 年龄
/// </summary>
public int Age { get; set; } /// <summary>
/// 地址
/// </summary>
[Required]
public string Address { get; set; } [StringLength(11, 11)]
public string PhoneNum { get; set; }
}

调用方式

UserEntity user = new UserEntity();
user.Validate();

上面的校验逻辑写的比较复杂,主要是考虑到对象中包含复杂对象的情况,如果都是简单对象,可以不用考虑,只需针对单个属性做字段校验

现有的方式是在校验不通过的时候抛出异常,此处大家也可以自定义异常来表示校验的问题,也可以返回自定义的校验结果实体来记录当前是哪个字段出的问题,留待大家自己实现

如果您有更好的建议和想法欢迎提出,共同进步

以上代码均为原创分享,若大家认为有不妥的地方,烦请留言指出,在下感激不尽

本文作者:hexuwsbg

出处:https://www.cnblogs.com/hexu0512/p/12879671.html

版权:本文采用「可附带出处转载」知识共享许可协议进行许可

.NET Attribute在数据校验上的应用的更多相关文章

  1. 移动商城第四篇【Controller配置、添加品牌之文件上传和数据校验】

    Controller层配置 编写SpringMVC的配置文件 springmvc.xml <?xml version="1.0" encoding="UTF-8&q ...

  2. springMVC数据校验与单文件上传

    spring表单标签:    <fr:from/> 渲染表单元素    <fr:input/>输入框组件    <fr:password/>密码框组件标签    & ...

  3. 学习SpringMVC必知必会(7)~springmvc的数据校验、表单标签、文件上传和下载

    输入校验是 Web 开发任务之一,在 SpringMVC 中有两种方式可以实现,分别是使用 Spring 自带的验证 框架和使用 JSR 303 实现, 也称之为 spring-validator 和 ...

  4. Tapdata Cloud 版本上新!率先支持数据校验、类型映射等6大新功能

    Tapdata Cloud cloud.tapdata.net Tapdata Cloud 是国内首家异构数据库实时同步云平台,目前支持 Oracle.MySQL.PG.SQL Server.Mong ...

  5. SpringMVC——数据转换 & 数据格式化 & 数据校验

    一.数据绑定流程 1. Spring MVC 主框架将 ServletRequest 对象及目标方 法的入参实例传递给 WebDataBinderFactory 实例,以创 建 DataBinder ...

  6. Struts2数据校验

    Struts2数据校验 1.常见数据校验方法 表单数据的校验方式: 表单中的数据必须被效验以后才能够被使用,常用的效验方式分为两种: 前台校验:也称之为客户端效验,主要是通过JS编程的方式进行表单数据 ...

  7. Struts 2 数据校验要用到的类和两种校验方式以及一些校验问题的解决

    通过继承ActionSupport类来完成Action开发,ActionSupport类不仅对Action接口进行简单实现, 同时增加了验证.本地化等支持 .真实开发中自定义Action都需要继承该类 ...

  8. WPF使用IDataErrorInfo进行数据校验

    这篇博客将介绍如何使用IDataErrorInfo进行数据校验.下面直接看例子.一个Customer类,两个属性(FirstName, Age) class Customer { public str ...

  9. Java数据校验(Bean Validation / JSR303)

    文档: http://beanvalidation.org/1.1/spec/ API : http://docs.jboss.org/hibernate/beanvalidation/spec/1. ...

随机推荐

  1. A Bug's Life POJ 2492

    D - A Bug's Life 二分图 并查集 BackgroundProfessor Hopper is researching the sexual behavior of a rare spe ...

  2. vue2.x学习笔记(八)

    接着前面的内容:https://www.cnblogs.com/yanggb/p/12577433.html. 列表渲染 vue提供了一个[v-for]指令用于列表渲染(循环). 用[v-for]指令 ...

  3. vs 基础

    1     写入 读取: 1)  写入:Console.Write("hello china")                                      光标紧跟 ...

  4. [PHP] 文件创建、写入、读取

    创建$p = fopen('text.txt','a+b'); 写入第一种方式//var_export方式存储数组到文件中 //这中方式存浮点型数据,存储后会多很多数字!只适合简单的存储吧!我感觉! ...

  5. 机器学习5- 对数几率回归+Python实现

    目录 1. 对数几率回归 1.1 求解 ω 和 b 2. 对数几率回归进行垃圾邮件分类 2.1 垃圾邮件分类 2.2 模型评估 混淆举证 精度 交叉验证精度 准确率召回率 F1 度量 ROC AUC ...

  6. 类内部装饰器的使用:property、classmethod与staticmethod

    1.property property是一种特殊的属性,可实现把函数名变为属性名使用.它可以在不改变类接口的前提下使用存取方法 (即读值和取值) 来修改数据的属性,property类有3个方法gett ...

  7. 为什么redis是单线程的以及为什么这么快?

    官网的说法 我们先来认真看一下官网的说法.翻译过来大意如下: CPU并不是您使用Redis的瓶颈,因为通常Redis要么受内存限制,要么受网络限制.例如,使用在一般Linux系统上运行的流水线Redi ...

  8. Junit借助Groboutils Core进行并发测试

    本文参考:http://www.voidcn.com/article/p-ybnvuffh-ke.html:转载请注明出处 junit是无法进行并发测试,但是又有需要并发测试的场景怎么办呢?此时可以借 ...

  9. 第 3 篇:实现博客首页文章列表 API

    作者:HelloGitHub-追梦人物 文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 此前在讨论基于模板引擎的开发方式和 django-rest-framework 开发 ...

  10. 【集群实战】NFS网络文件共享服务3-相关知识补充(showmount,exports,rpc)

    1. showmount命令说明 showmount命令一般用于从NFS客户端检查NFS服务器端共享目录的情况. 参数说明: -e,--exports 显示NFS服务器输出的目录列表 [root@we ...