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
相关产品推荐
相关产品推荐

