热门标签 | 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在数据校验的应用内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 本文将通过一个简单的示例代码,介绍如何在 Java 中获取对象中值为 null 的字段名称。 ... [详细]
  • ABP框架是ASP.NET Boilerplate的简称,它不仅是一个开源且文档丰富的应用程序框架,还提供了一套基于领域驱动设计(DDD)的最佳实践架构模型。本文将详细介绍ABP框架的特点、项目结构及其在Web API优先架构中的应用。 ... [详细]
  • 近期遇到了一个系统中在线文本编辑器上传本地图片时报服务器异常的问题。经过初步调试和排查,最终找到了解决方案。 ... [详细]
  • 本文介绍了如何使用Postman构建和发送HTTP请求,包括四个主要部分:方法(Method)、URL、头部(Headers)和主体(Body)。特别强调了Body部分的重要性,并详细说明了不同类型的请求体。 ... [详细]
  • vue引入echarts地图的四种方式
    一、vue中引入echart1、安装echarts:npminstallecharts--save2、在main.js文件中引入echarts实例:  Vue.prototype.$echartsecharts3、在需要用到echart图形的vue文件中引入:   importechartsfrom&amp;quot;echarts&amp;quot;;4、如果用到map(地图),还 ... [详细]
  • Bootstrap 插件使用指南
    本文详细介绍了如何在 Web 前端开发中使用 Bootstrap 插件,包括自动触发插件的方法、插件的引用方式以及具体的实例。 ... [详细]
  • 如何解决TS1219:实验性装饰器功能可能在未来版本中更改的问题
    本文介绍了两种方法来解决TS1219错误:通过VSCode设置启用实验性装饰器,或在项目根目录下创建配置文件(jsconfig.json或tsconfig.json)。 ... [详细]
  • MongoDB核心概念详解
    本文介绍了NoSQL数据库的概念及其应用场景,重点解析了MongoDB的基本特性、数据结构以及常用操作。MongoDB是一个高性能、高可用且易于扩展的文档数据库系统。 ... [详细]
  • 本文介绍了如何查看PHP网站及其源码的方法,包括环境搭建、本地测试、源码查看和在线查找等步骤。 ... [详细]
  • 小程序的授权和登陆
    小程序的授权和登陆 ... [详细]
  • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
  • 深入解析Django CBV模型的源码运行机制
    本文详细探讨了Django CBV(Class-Based Views)模型的源码运行流程,通过具体的示例代码和详细的解释,帮助读者更好地理解和应用这一强大的功能。 ... [详细]
  • 在OpenShift上部署基于MongoDB和Node.js的多层应用程序
    本文档详细介绍了如何在OpenShift 4.x环境中部署一个包含MongoDB数据库和Node.js后端及前端的多层应用程序。通过逐步指导,读者可以轻松完成整个部署过程。 ... [详细]
  • 本文详细介绍了如何在PHP中记录和管理行为日志,包括ThinkPHP框架中的日志记录方法、日志的用途、实现原理以及相关配置。 ... [详细]
  • 2022年2月 微信小程序 app.json 配置详解:启用调试模式
    本文将详细介绍如何在微信小程序的 app.json 文件中启用调试模式(debug),并通过实际案例展示其配置方法和应用场景。 ... [详细]
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社区 版权所有