热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

关于.NETAttribute在数据校验中的应用教程

这篇文章主要给大家介绍了关于.NETAttribute在数据校验中的应用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用.NET具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

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
 {
   /// 
   /// 姓名
   /// 
   [FieldDescription("用户名称")]
   public string Name { get; set; }

   /// 
   /// 年龄
   /// 
   public int Age { get; set; }

   /// 
   /// 地址
   /// 
   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
  {
    /// 
    /// 校验后的错误信息
    /// 
    public string ErrorMessage { get; set; }

    /// 
    /// 数据校验
    /// 
    /// 
    public abstract void Validate(object value);
  }

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

    /// 
    /// 非空校验
    /// 
    [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) &#63; "字段不能为空" : ErrorMessage);
        }
      }
    }

    /// 
    /// 自定义验证,验证字符长度
    /// 
    [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) &#63; $"字段长度必须在{_minLength}与{_maxLength}之间" : ErrorMessage);
      }
    }

添加一个用于校验的ValidateExtensions

public static class ValidateExtensions
 {
   /// 
   /// 校验
   /// 
   /// 
   /// 
   public static void Validate(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
 {
   /// 
   /// 姓名
   /// 
   [Required]
   public string Name { get; set; }

   /// 
   /// 年龄
   /// 
   public int Age { get; set; }

   /// 
   /// 地址
   /// 
   [Required]
   public string Address { get; set; }

   [StringLength(11, 11)]
   public string PhoneNum { get; set; }
 }

调用方式

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

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

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

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

总结

到此这篇关于.NET Attribute在数据校验中的应用的文章就介绍到这了,更多相关.NET Attribute在数据校验的应用内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • jmeter实践:从csv中获取带引号的数据详情的技巧和运行全部数据的方法
    本文分享了jmeter实践中从csv中获取带引号的数据的解决办法,包括设置CSV Data Set Config和运行脚本获取数据的方法。另外还介绍了循环运行csv中全部数据的解决方法,避免每次修改csv用例都需要修改脚本的麻烦。通过了解和掌握工具的细节点,可以更好地解决问题和提高技术水平。 ... [详细]
  • 单点登录原理及实现方案详解
    本文详细介绍了单点登录的原理及实现方案,其中包括共享Session的方式,以及基于Redis的Session共享方案。同时,还分享了作者在应用环境中所遇到的问题和经验,希望对读者有所帮助。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • React项目中运用React技巧解决实际问题的总结
    本文总结了在React项目中如何运用React技巧解决一些实际问题,包括取消请求和页面卸载的关联,利用useEffect和AbortController等技术实现请求的取消。文章中的代码是简化后的例子,但思想是相通的。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
author-avatar
核电列兵_851
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有