导读:本期聚焦于小伙伴创作的《如何利用.NET的特性和反射实现数据唯一性检查》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何利用.NET的特性和反射实现数据唯一性检查》有用,将其分享出去将是对创作者最好的鼓励。

在.NET应用开发中,数据唯一性校验是保障业务数据规范的重要环节。通过自定义特性标注需要校验唯一性的字段,再结合反射动态读取特性配置,能够实现一个通用的数据唯一性检查方案,减少重复代码量。

如何利用.NET的特性和反射实现数据唯一性检查

一、自定义唯一性校验特性

首先我们需要定义一个自定义特性,用来标记需要进行唯一性检查的实体类属性,同时可以配置一些校验相关的参数,比如校验提示信息、是否忽略空值等。

using System;

namespace UniqueCheckDemo.Attributes
{
    /// <summary>
    /// 标记属性需要进行唯一性校验的特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class UniqueCheckAttribute : Attribute
    {
        /// <summary>
        /// 校验失败的提示信息
        /// </summary>
        public string ErrorMessage { get; set; }

        /// <summary>
        /// 是否忽略空值,默认忽略
        /// </summary>
        public bool IgnoreNull { get; set; } = true;

        public UniqueCheckAttribute(string errorMessage = "")
        {
            ErrorMessage = errorMessage;
        }
    }
}

二、模拟数据仓储与校验上下文

为了演示唯一性校验的逻辑,我们简单模拟一个数据仓储,用来模拟查询数据库中已存在的数据,实际项目中可以替换为真实的数据库查询逻辑。

using System;
using System.Collections.Generic;
using System.Linq;

namespace UniqueCheckDemo.Data
{
    /// <summary>
    /// 模拟用户数据实体
    /// </summary>
    public class User
    {
        public int Id { get; set; }

        public string UserName { get; set; }

        public string Email { get; set; }
    }

    /// <summary>
    /// 模拟用户数据仓储
    /// </summary>
    public class UserRepository
    {
        // 模拟数据库已存在的用户数据
        private readonly List<User> _existingUsers = new List<User>
        {
            new User { Id = 1, UserName = "zhangsan", Email = "zhangsan@ipipp.com" },
            new User { Id = 2, UserName = "lisi", Email = "lisi@ipipp.com" }
        };

        /// <summary>
        /// 检查指定属性值是否已存在
        /// </summary>
        /// <param name="propertyName">属性名称</param>
        /// <param name="value">属性值</param>
        /// <param name="excludeId">排除的实体ID,用于更新场景</param>
        /// <returns>是否已存在</returns>
        public bool IsPropertyValueExists(string propertyName, object value, int? excludeId = null)
        {
            if (value == null)
            {
                return false;
            }

            return propertyName switch
            {
                "UserName" => _existingUsers.Any(u => u.UserName == value.ToString() && (!excludeId.HasValue || u.Id != excludeId.Value)),
                "Email" => _existingUsers.Any(u => u.Email == value.ToString() && (!excludeId.HasValue || u.Id != excludeId.Value)),
                _ => false
            };
        }
    }
}

三、反射实现通用唯一性校验逻辑

接下来我们通过反射读取实体类上标记的特性,动态执行唯一性检查,返回校验结果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UniqueCheckDemo.Attributes;
using UniqueCheckDemo.Data;

namespace UniqueCheckDemo.Validation
{
    /// <summary>
    /// 唯一性校验结果
    /// </summary>
    public class UniqueCheckResult
    {
        public bool IsSuccess { get; set; }

        public List<string> ErrorMessages { get; set; } = new List<string>();
    }

    /// <summary>
    /// 通用唯一性校验器
    /// </summary>
    public class UniqueValidator
    {
        private readonly UserRepository _userRepository = new UserRepository();

        /// <summary>
        /// 校验实体类的唯一性
        /// </summary>
        /// <param name="entity">待校验的实体</param>
        /// <param name="excludeId">排除的实体ID,用于更新场景</param>
        /// <returns>校验结果</returns>
        public UniqueCheckResult ValidateUnique(object entity, int? excludeId = null)
        {
            var result = new UniqueCheckResult { IsSuccess = true };
            Type entityType = entity.GetType();
            // 获取实体类的所有属性
            PropertyInfo[] properties = entityType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (PropertyInfo property in properties)
            {
                // 获取属性上的UniqueCheckAttribute特性
                var uniqueAttr = property.GetCustomAttribute<UniqueCheckAttribute>();
                if (uniqueAttr == null)
                {
                    continue;
                }

                // 获取属性值
                object propertyValue = property.GetValue(entity);
                // 如果忽略空值且属性值为空,跳过校验
                if (uniqueAttr.IgnoreNull && propertyValue == null)
                {
                    continue;
                }

                // 调用仓储检查方法判断值是否已存在
                bool isExists = _userRepository.IsPropertyValueExists(property.Name, propertyValue, excludeId);
                if (isExists)
                {
                    result.IsSuccess = false;
                    string errorMsg = string.IsNullOrEmpty(uniqueAttr.ErrorMessage) 
                        ? $"{property.Name} 值已存在,请更换后重试" 
                        : uniqueAttr.ErrorMessage;
                    result.ErrorMessages.Add(errorMsg);
                }
            }

            return result;
        }
    }
}

四、实际使用示例

下面我们定义一个用户实体类,在需要校验唯一性的属性上标记UniqueCheckAttribute特性,然后调用校验器完成校验。

using System;
using UniqueCheckDemo.Attributes;
using UniqueCheckDemo.Validation;

namespace UniqueCheckDemo
{
    /// <summary>
    /// 用户实体类
    /// </summary>
    public class UserDto
    {
        public int Id { get; set; }

        [UniqueCheck(ErrorMessage = "用户名已存在,请更换其他用户名")]
        public string UserName { get; set; }

        [UniqueCheck(ErrorMessage = "邮箱已存在,请使用其他邮箱")]
        public string Email { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var validator = new UniqueValidator();

            // 测试新增场景:用户名已存在
            var newUser = new UserDto
            {
                UserName = "zhangsan",
                Email = "new_email@ipipp.com"
            };
            var addResult = validator.ValidateUnique(newUser);
            Console.WriteLine($"新增校验结果:{addResult.IsSuccess}");
            if (!addResult.IsSuccess)
            {
                foreach (var error in addResult.ErrorMessages)
                {
                    Console.WriteLine(error);
                }
            }

            // 测试更新场景:排除当前用户ID后校验
            var updateUser = new UserDto
            {
                Id = 1,
                UserName = "zhangsan",
                Email = "zhangsan_new@ipipp.com"
            };
            var updateResult = validator.ValidateUnique(updateUser, updateUser.Id);
            Console.WriteLine($"更新校验结果:{updateResult.IsSuccess}");
            if (!updateResult.IsSuccess)
            {
                foreach (var error in updateResult.ErrorMessages)
                {
                    Console.WriteLine(error);
                }
            }
        }
    }
}

五、方案总结

这种基于特性和反射的实现方式,将唯一性校验的逻辑与业务代码完全解耦,后续如果需要新增校验的字段,只需要在对应的属性上添加UniqueCheckAttribute特性即可,不需要修改校验的核心逻辑。如果后续需要支持更多的校验规则,也可以扩展特性属性,在反射处理逻辑中增加对应的判断分支,具备很好的扩展性。

需要注意的是,反射操作会有一定的性能开销,如果对性能要求较高的场景,可以结合缓存机制,将反射获取的类型特性信息缓存起来,避免重复反射带来的性能损耗。

.NET特性反射数据唯一性检查修改时间:2026-06-04 14:35:58

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。