EF Core如何按指定字段组合识别重复数据并阻止重复插入
EF Core 基于Name+Surname组合去重阻止重复入库实现方案
你的去重规则核心是Name和Surname两个字段组合全局唯一,和Citizenship字段取值无关,需要从「数据库约束兜底、EF Core配置、业务前置校验、并发异常处理」四个层面落地,彻底避免重复数据入库。
1. 配置复合唯一索引(最核心兜底方案)
这个方案会在数据库层面生成强唯一约束,就算应用层逻辑出现疏漏、或者高并发下出现竞态请求,数据库也会直接拒绝重复组合写入,是防重复的最可靠手段,必须配置。
在你的DbContext上下文类中重写OnModelCreating方法做如下配置:
public class AppDbContext : DbContext { public DbSet<Person> Persons { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置Name+Surname复合唯一索引,可自定义索引名 modelBuilder.Entity<Person>() .HasIndex(p => new { p.Name, p.Surname }) .IsUnique() .HasDatabaseName("IX_Persons_Name_Surname_Unique"); } }
配置完成后执行EF Core迁移命令生成迁移脚本、更新数据库,唯一索引才会实际生效。
2. 插入前前置校验(提前拦截重复请求)
数据库兜底虽然可靠,但抛出异常会带来额外性能开销,正常流量下建议在业务逻辑层先做一次重复校验,提前拦截重复请求:
public async Task<(bool Success, string Msg)> AddPersonAsync(Person newPerson, CancellationToken ct) { // 校验是否已存在同Name+Surname的记录 bool existsDuplicate = await _dbContext.Persons .AnyAsync(p => p.Name == newPerson.Name && p.Surname == newPerson.Surname, ct); if (existsDuplicate) { // 可根据业务需求直接返回错误、记录日志或抛自定义业务异常 return (false, "已存在相同姓名的人员记录,不可重复添加"); } _dbContext.Persons.Add(newPerson); await _dbContext.SaveChangesAsync(ct); return (true, string.Empty); }
3. 并发场景异常捕获处理
高并发场景下可能出现两个请求同时通过前置校验、同时执行插入的竞态情况,这时候数据库唯一索引会抛出更新异常,需要单独捕获处理,避免程序直接报错:
public async Task<(bool Success, string Msg)> SavePersonWithCheckAsync(Person newPerson, CancellationToken ct) { try { _dbContext.Persons.Add(newPerson); await _dbContext.SaveChangesAsync(ct); return (true, string.Empty); } catch (DbUpdateException ex) when (IsUniqueIndexConflict(ex)) { // 捕获唯一索引冲突异常,判定为重复数据 return (false, "已存在相同姓名的人员记录,不可重复添加"); } } // 适配不同数据库的唯一约束冲突错误码 private bool IsUniqueIndexConflict(DbUpdateException ex) { var errorCode = ex.InnerException switch { Microsoft.Data.SqlClient.SqlException sqlEx => sqlEx.Number, Npgsql.PostgresException pgEx => int.Parse(pgEx.SqlState), MySqlConnector.MySqlException mysqlEx => mysqlEx.Number, _ => 0 }; return errorCode switch { 2601 or 2627 => true, // SQL Server 唯一索引/约束冲突错误码 23505 => true, // PostgreSQL 唯一冲突错误码 1062 => true, // MySQL 唯一冲突错误码 _ => false }; }
注意事项
- 不要只靠应用层校验做去重,没有数据库唯一索引兜底的话,高并发下一定会出现重复数据
- 如果需要做大小写不敏感的姓名去重,可以在配置索引时指定对应排序规则,比如SQL Server环境下追加
.UseCollation("SQL_Latin1_General_CP1_CI_AS")即可 - 后续如果调整去重规则,只需要修改唯一索引对应的字段组合,重新生成迁移更新数据库即可
内容的提问来源于stack exchange,提问作者Chris Claude
相关产品推荐
相关产品推荐

