.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
相关产品推荐
相关产品推荐

