EF Core中基于条件实现Upsert(存在更新不存在插入)的方案咨询
问题根因
你生成的SQL本身逻辑是符合MySQL的Upsert语法的,但ON DUPLICATE KEY UPDATE机制只有在插入操作触发主键/唯一键冲突时才会执行更新逻辑。你当前场景下UserName和Key两个字段没有配置联合唯一索引,数据库不会判定插入的记录重复,所以会一直插入新行。
修复步骤
1. 给实体配置联合唯一索引
在你的DbContext的OnModelCreating方法中添加索引约束,之后执行EF Core迁移将规则同步到数据库:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<UserStateEntity>() .HasIndex(u => new { u.UserName, u.Key }) .IsUnique(); // 必须指定唯一约束才会触发冲突判定 }
2. 修正FlexLabs.Upsert的使用代码
你的原有代码存在两个冗余/错误点:
RunAsync()方法本身已经包含提交逻辑,不需要额外调用SaveChangesAsync()- 实体类型对应错误,你定义的实体是
UserStateEntity,更新语句中错误使用了UserDataEntity
修正后代码如下:
public async Task UpdateStateAsync() { var userData = new UserStateEntity { UserName = "123", Key = "456", Value = "MainMenu" }; await dbContext .UserCache // 和你DbSet定义的属性名保持一致 .Upsert(userData) .On(ud => new {ud.UserName, ud.Key}) .WhenMatched(v => new UserStateEntity {Value = userData.Value}) .RunAsync(); }
无第三方依赖的原生EF Core实现方案
如果你不想引入FlexLabs.Upsert库,可以在加了唯一索引的前提下,用事务+异常捕获优化原有实现,解决并发场景下的重复插入问题:
public async Task AddCacheValueAsync(string userName, string key, string value) { using var transaction = await dbContext.Database.BeginTransactionAsync(); try { var existedData = await dbContext.UserCache .SingleOrDefaultAsync(d => d.UserName == userName && d.Key == key); if (existedData != null) { existedData.Value = value; } else { dbContext.UserCache.Add(new UserStateEntity { UserName = userName, Key = key, Value = value }); } await dbContext.SaveChangesAsync(); await transaction.CommitAsync(); } catch (DbUpdateException) { // 并发插入冲突时重试更新逻辑 var existedData = await dbContext.UserCache .SingleAsync(d => d.UserName == userName && d.Key == key); existedData.Value = value; await dbContext.SaveChangesAsync(); await transaction.CommitAsync(); } }
内容的提问来源于stack exchange,提问作者Vadim Martynov
相关产品推荐
相关产品推荐

