.NET Attribute在数据校验上的应用

简介:

.NET Attribute在数据校验上的应用

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

相关文章
|
2月前
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
130 10
|
11天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
24天前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
|
1月前
|
存储 编译器
.Net特性Attribute的高级使用
【10月更文挑战第14天】在.NET中,特性(Attribute)是一种强大的机制,用于在代码中添加元数据。本文介绍了特性的高级用法,包括自定义特性、通过反射读取特性、条件编译与特性结合、多个特性应用以及特性继承。通过示例展示了如何创建自定义特性类、应用自定义特性,并通过反射获取特性信息。此外,还介绍了如何利用条件编译符号实现不同版本的代码控制,以及如何在一个代码元素上应用多个特性。最后,探讨了如何通过`AttributeUsage`控制特性的继承行为。
|
2月前
|
数据采集 JSON API
.NET 3.5 中 HttpWebRequest 的核心用法及应用
【9月更文挑战第7天】在.NET 3.5环境下,HttpWebRequest 类是处理HTTP请求的一个核心组件,它封装了HTTP协议的细节,使得开发者可以方便地发送HTTP请求并接收响应。本文将详细介绍HttpWebRequest的核心用法及其实战应用。
127 6
|
3月前
|
前端开发 JavaScript 开发工具
跨域联姻:React.NET——.NET应用与React的完美融合,解锁前后端高效协作新姿势。
【8月更文挑战第28天】探索React.NET,这是将热门前端框架React与强大的.NET后端无缝集成的创新方案。React以其组件化和虚拟DOM技术著称,能构建高性能、可维护的用户界面;.NET则擅长企业级应用开发。React.NET作为桥梁,使.NET应用轻松采用React构建前端,并优化开发流程与性能。通过直接托管React组件,.NET应用简化了部署流程,同时支持服务器端渲染(SSR),提升首屏加载速度与SEO优化。
82 1
|
3月前
|
存储 缓存 安全
.NET 在金融行业的应用:高并发交易系统的构建与优化之路
【8月更文挑战第28天】在金融行业,交易系统需具备高并发处理、低延迟及高稳定性和安全性。利用.NET构建此类系统时,可采用异步编程提升并发能力,优化数据库访问以降低延迟,使用缓存减少数据库访问频率,借助分布式事务确保数据一致性,并加强安全性措施。通过综合优化,满足金融行业的严苛要求。
50 1
|
3月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
58 0
|
3月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
68 0
|
3月前
|
Java Spring 自然语言处理
Spring 框架里竟藏着神秘魔法?国际化与本地化的奇妙之旅等你来揭开谜底!
【8月更文挑战第31天】在软件开发中,国际化(I18N)与本地化(L10N)对于满足不同地区用户需求至关重要。Spring框架提供了强大支持,利用资源文件和`MessageSource`实现多语言文本管理。通过配置日期格式和货币符号,进一步完善本地化功能。合理应用这些特性,可显著提升应用的多地区适应性和用户体验。
41 0

热门文章

最新文章

下一篇
无影云桌面