ASP.NET Core Identity并发创建用户重复邮箱与锁内异步问题咨询
解决ASP.NET Core Identity并发创建重复邮箱用户的问题
问题根源
你设置的RequireUniqueEmail = true仅在业务层做前置检查,并发场景下会出现竞态条件:多个请求同时通过“邮箱是否存在”的检查,随后都执行插入操作。如果数据库的Email字段没有唯一约束,就会生成重复数据。而lock关键字不支持异步代码,所以直接用lock包裹await方法会报错。
解决方案
1. 数据库层面添加唯一约束(最可靠的最终防线)
通过EF Core的Fluent API给User实体的Email字段配置唯一索引,让数据库强制保证邮箱唯一性:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 给Email字段添加唯一约束 modelBuilder.Entity<User>() .HasIndex(u => u.Email) .IsUnique(); }
执行EF迁移并更新数据库后,数据库会直接拦截重复邮箱的插入请求,抛出约束冲突异常。
2. 业务层用异步同步原语替代lock
使用SemaphoreSlim实现异步锁,保证同一时间只有一个请求执行“邮箱检查+用户创建”的逻辑:
// 定义静态信号量,初始计数1,最多允许1个请求进入临界区 private static readonly SemaphoreSlim _createUserSemaphore = new SemaphoreSlim(1, 1); public async Task<IdentityResult> CreateUniqueUserAsync(User userToCreate, string userPassword) { try { // 等待进入临界区 await _createUserSemaphore.WaitAsync(); // 先检查邮箱是否已存在 var existingUser = await _userManager.FindByEmailAsync(userToCreate.Email); if (existingUser != null) { return IdentityResult.Failed(new IdentityError { Description = "邮箱已存在" }); } // 执行用户创建 return await _userManager.CreateAsync(userToCreate, userPassword); } finally { // 无论成功失败,都释放信号量 _createUserSemaphore.Release(); } }
3. 捕获数据库约束异常并转换为业务错误
即使有业务层的异步锁,极端情况(比如外部直接操作数据库)仍可能触发数据库约束,需要捕获异常并转换为用户能理解的错误:
public async Task<IdentityResult> CreateUniqueUserAsync(User userToCreate, string userPassword) { try { await _createUserSemaphore.WaitAsync(); var existingUser = await _userManager.FindByEmailAsync(userToCreate.Email); if (existingUser != null) { return IdentityResult.Failed(new IdentityError { Description = "邮箱已存在" }); } return await _userManager.CreateAsync(userToCreate, userPassword); } catch (DbUpdateException ex) { // 识别SQL Server唯一约束冲突错误(错误码2601) if (ex.InnerException is SqlException sqlEx && sqlEx.Number == 2601) { return IdentityResult.Failed(new IdentityError { Description = "邮箱已存在" }); } // 其他异常重新抛出 throw; } finally { _createUserSemaphore.Release(); } }
最优实践
推荐同时使用数据库唯一约束 + 业务层异步锁 + 异常捕获的三层保障,既从根源阻止重复数据,又保证业务层的友好错误提示。
内容的提问来源于stack exchange,提问作者Milad Ahmadi
相关产品推荐
相关产品推荐

