You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core更新用户数据时相同邮箱提示已存在如何解决

问题描述

在使用MongoDB与.NET Core开发API时,自定义邮箱唯一性验证出现场景适配问题:

  • 新建用户场景下验证逻辑运行正常
  • 更新用户场景下,仅当修改Email为新值时验证可正常通过;如果提交时保留用户原有邮箱,会误触发「邮箱已被使用」的验证错误

涉及的原有代码如下:

原有自定义邮箱唯一验证特性

public class EmailUserUniqueAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        UserService? _context = validationContext.GetService(typeof(UserService)) as UserService;
        var entity = _context?.Users().Find(x => x.Email == value.ToString()).FirstOrDefault();

        if (entity != null)
        {
            return new ValidationResult(GetErrorMessage(value.ToString()));
        }

        return ValidationResult.Success;
    }

    public string GetErrorMessage(string email)
    {
        return $"Email {email} is already in use.";
    }
}

原有更新用户请求DTO

public class UpdateUserRequestDto
{
    [Required]
    public string UserName { get; set; }
    [Required,DataType(DataType.EmailAddress),EmailUserUnique]
    public string Email { get; set; }
    [Required, MinimumAgeValidator(18)]
    public DateTime DateOfBirth { get; set; }
}
问题原因

原有验证逻辑仅判断「数据库中是否存在同邮箱的用户记录」,没有在更新场景下排除当前正在编辑的用户自身。当更新请求提交原有邮箱时,查询会直接匹配到当前用户自己的存储记录,误判为邮箱重复。

解决方案

调整验证逻辑,支持在更新场景下排除当前操作的用户自身记录,具体实现步骤如下:

  • 修改EmailUserUniqueAttribute特性,增加可选参数用于指定DTO中存储当前用户ID的属性名,验证时读取该ID值,在邮箱重复查询时过滤掉ID匹配的自身记录
  • 为更新请求DTO增加当前被编辑的用户ID字段,给验证特性配置对应的ID属性名映射

修改后的验证特性代码:

public class EmailUserUniqueAttribute : ValidationAttribute
{
    /// <summary>
    /// DTO中存储当前操作用户ID的属性名,更新场景下用于排除自身记录
    /// </summary>
    public string? UserIdPropertyName { get; set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        UserService? _context = validationContext.GetService(typeof(UserService)) as UserService;
        if (_context == null || value == null)
        {
            return ValidationResult.Success;
        }

        string email = value.ToString()!;
        // 基础查询条件:匹配相同邮箱
        var filter = Builders<User>.Filter.Eq(u => u.Email, email);

        // 存在用户ID配置时,追加排除自身的查询条件
        if (!string.IsNullOrEmpty(UserIdPropertyName))
        {
            var userIdProp = validationContext.ObjectType.GetProperty(UserIdPropertyName);
            if (userIdProp != null)
            {
                var currentUserId = userIdProp.GetValue(validationContext.ObjectInstance)?.ToString();
                if (!string.IsNullOrEmpty(currentUserId))
                {
                    filter &= Builders<User>.Filter.Ne(u => u.Id, currentUserId);
                }
            }
        }

        var duplicateUser = _context.Users().Find(filter).FirstOrDefault();
        return duplicateUser == null 
            ? ValidationResult.Success 
            : new ValidationResult(GetErrorMessage(email));
    }

    public string GetErrorMessage(string email)
    {
        return $"Email {email} is already in use.";
    }
}

修改后的更新请求DTO代码:

public class UpdateUserRequestDto
{
    /// <summary>
    /// 当前被更新的用户ID
    /// </summary>
    [Required]
    public string Id { get; set; }
    [Required]
    public string UserName { get; set; }
    [Required,DataType(DataType.EmailAddress),EmailUserUnique(UserIdPropertyName = nameof(Id))]
    public string Email { get; set; }
    [Required, MinimumAgeValidator(18)]
    public DateTime DateOfBirth { get; set; }
}

适配说明

  • 新建用户场景下不需要传ID字段,验证逻辑不会追加排除条件,保持原有全局邮箱唯一校验能力
  • 更新用户场景下,只需要将路由或请求参数中携带的当前用户ID绑定到DTO的Id属性即可,不需要额外调整前端传参逻辑
  • 如果你的User实体主键不是string类型(如ObjectId、Guid),只需要对应调整过滤条件中ID的类型匹配逻辑即可

内容的提问来源于stack exchange,提问作者Moonwar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 10:24:14