EF Core 6偶发重复插入多条相同记录问题求助
解决Razor Pages + EF Core 6表单偶发重复插入问题
问题分析
你遇到的偶发重复插入(几百次提交出现一次),两条记录仅Id和UpdatedDate(相差1秒)不同,核心原因几乎可以确定是重复提交请求:
- 用户快速连续点击提交按钮
- 网络波动导致浏览器自动重试未收到响应的请求
解决方案
1. 前端层面:阻止重复点击
点击提交按钮后立即禁用按钮,从源头避免重复提交:
<input type="submit" value="Save" class="btn btn-primary" asp-route-mode="@Model.Mode" style="margin-top:15px; padding:5px;" onclick="this.disabled=true; this.form.submit();" />
或者用更健壮的JS监听表单提交事件:
document.querySelector('form').addEventListener('submit', function() { const submitBtn = this.querySelector('input[type="submit"]'); submitBtn.disabled = true; submitBtn.value = 'Saving...'; });
2. 后端层面:实现幂等校验
前端防护可能被绕过,后端需要确保同一请求不会被处理多次,两种常用方案:
方案A:请求唯一令牌校验
- 在页面生成唯一令牌,通过隐藏域提交:
<input type="hidden" name="RequestToken" value="@Guid.NewGuid().ToString()" />
- 后端接收后,用缓存记录已处理的令牌(设置合理过期时间):
public async Task<IActionResult> OnPostAsync(string requestToken) { // 检查令牌是否已处理 if (await _memoryCache.GetStringAsync(requestToken) != null) { ModelState.AddModelError(string.Empty, "请求已处理,请勿重复提交"); return Page(); } // 标记令牌已处理,5分钟后过期 await _memoryCache.SetStringAsync(requestToken, "processed", TimeSpan.FromMinutes(5)); // 原有保存逻辑 Rate.UpdatedDate = DateTime.UtcNow; _context.MedBAmounts.Add(Rate); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }
方案B:业务字段唯一约束
如果业务上NewAmount结合其他关联字段(比如用户ID、关联实体ID)应该唯一,给数据库表添加唯一约束,后端捕获冲突异常:
try { Rate.UpdatedDate = DateTime.UtcNow; _context.MedBAmounts.Add(Rate); await _context.SaveChangesAsync(); } catch (DbUpdateException ex) { // 捕获SQL Server唯一约束冲突错误码(2601) if (ex.InnerException is SqlException sqlEx && sqlEx.Number == 2601) { ModelState.AddModelError(string.Empty, "该记录已存在,请勿重复提交"); return Page(); } throw; // 其他异常正常抛出 }
3. 优化UpdatedDate设置
用DateTime.UtcNow代替DateTime.Now避免时区问题,更推荐让数据库自动生成该字段:
// 实体类配置 public class MedBAmount { // 其他字段 public DateTime UpdatedDate { get; set; } } // 在DbContext的OnModelCreating中配置默认值 protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<MedBAmount>() .Property(x => x.UpdatedDate) .HasDefaultValueSql("GETUTCDATE()"); }
之后后端无需手动设置UpdatedDate,由数据库自动生成,减少代码冗余。
内容的提问来源于stack exchange,提问作者user3496144
相关产品推荐
相关产品推荐

