You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF7保存数据触发UNIQUE KEY约束冲突问题求助

解决EF7保存时唯一键约束冲突问题

问题背景

我给dbo.AccountDataTransactions表添加了唯一键约束,SQL语句如下:

IF NOT EXISTS (SELECT * FROM sys.sysconstraints WHERE constid = OBJECT_ID('UX_AccountDataTransaction_TransactionId')) BEGIN     
    ALTER TABLE dbo.AccountDataTransactions     
    ADD CONSTRAINT UX_AccountDataTransaction_TransactionId UNIQUE CLUSTERED (TransactionId) 
END 
GO 

通过EF7从外部服务获取数据保存时,持续报错:

Violation of UNIQUE KEY constraint 'UX_AccountDataTransaction_TransactionId'. Cannot insert duplicate key in object 'dbo.AccountDataTransactions'. The duplicate key value is (87000058395348).

保存前检查过待插入的TransactionId无重复,相关代码:

List<Wallet.Api.Repository.Db.Models.AccountDataTransaction> result = await SaveTransactionsAsync(accparams, cancellationToken);
_context.AttachRange(result);
_ = await _context.SaveChangesAsync();

实体模型定义:

public partial class AccountDataTransaction
{
    public long Id { get; set; }
    public string? AccountNumber { get; set; }
    public string? TransactionId { get; set; }
    public string? InstructionId { get; set; }
    public string? Source { get; set; }
    public decimal Amount { get; set; }
    public string? Direction { get; set; }
    public string? Status { get; set; }
    public decimal AmountLocal { get; set; }
    public string? Currency { get; set; }
    public DateTimeOffset? ValueDate { get; set; }
    public DateTimeOffset? TransactionTime { get; set; }
    public string? Description { get; set; }
    public string? TransactionKind { get; set; }
    public string? CreditorName { get; set; }
    public string? CreditorAddressLine { get; set; }
    public string? CreditorLocality { get; set; }
    public string? CreditorCountry { get; set; }
    public string? CreditorAccount { get; set; }
    public string? CreditorBank { get; set; }
    public string? DebtorName { get; set; }
    public string? DebtorAddressLine { get; set; }
    public string? DebtorLocality { get; set; }
    public string? DebtorCountry { get; set; }
    public string? DebtorAccount { get; set; }
    public string? DebtorBank { get; set; }
    public string? Purpose { get; set; }
    public string? PurposeCode { get; set; }
    public string? CreditorReference { get; set; }
    public DateTimeOffset? DateFrom { get; set; }
    public DateTimeOffset? DateTo { get; set; }
    public string? CreatedBy { get; set; }
    public DateTime CreationTime { get; set; }
}

可能的原因及解决方案

1. 数据库已存在重复的TransactionId

你仅检查了待插入列表的重复,但未确认数据库中是否已有TransactionId = '87000058395348'的记录。执行以下SQL验证:

SELECT * FROM dbo.AccountDataTransactions WHERE TransactionId = '87000058395348'

如果存在:

  • 若旧记录无效,可直接删除
  • 若需保留旧记录,修改待插入数据的TransactionId
  • 改用UPSERT逻辑(比如EF的AddOrUpdate方法,或SQL的MERGE语句)处理重复数据

2. AttachRange使用错误

AttachRange会将实体标记为Unchanged状态,适用于已存在于数据库的实体跟踪。如果是插入新数据,应该用AddRange,否则会导致EF状态跟踪混乱,甚至尝试更新不存在的实体引发冲突。修改代码:

// 替换AttachRange为AddRange
_context.AddRange(result);
await _context.SaveChangesAsync();

3. TransactionId数据类型不匹配

实体中TransactionId是string?类型,但数据库中该字段可能是数值类型(如bigint)。隐式类型转换可能导致不同字符串被转换为相同数值,引发重复。

  • 检查数据库TransactionId字段类型,确保与实体类型一致
  • 若数据库是bigint,将实体中TransactionId改为long?类型,避免转换冲突

4. 并发保存导致的冲突

如果多线程/进程同时执行保存操作,可能出现“检查时无重复,但SaveChanges前已被其他进程插入”的情况。解决方案:

  • 给保存操作加分布式锁,确保同一时间只有一个进程执行
  • 在SaveChanges时捕获异常,添加重试逻辑:捕获冲突后检查数据库是否已存在该记录,存在则跳过或更新

5. EF上下文状态跟踪异常

如果上下文之前已加载过相同TransactionId的实体,AttachRange会导致状态冲突。可以:

  • 保存前清理上下文跟踪:_context.ChangeTracker.Clear();
  • 使用全新的上下文实例执行保存操作

内容的提问来源于stack exchange,提问作者Zdravko Lukic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 22:20:32