调用SaveChanges时触发EF乐观并发错误,求排查解决思路
针对你遇到的EF批量增改时触发DbUpdateConcurrencyException且目标表无数据的问题,以下是具体的排查方向和修复方案:
1. 实体缺失主键导致EF无法定位更新目标
你的Record实体未定义主键,EF无法唯一标识实体,导致Update操作无法确定数据库中对应的行,最终触发影响0行的异常。业务上UserId和CourseId是唯一标识一条记录的字段,需配置为复合主键:
public class Record { [Key, Column(Order = 0)] public int UserId { get; set; } [Key, Column(Order = 1)] public int CourseID { get; set; } [ConcurrencyCheck] public int CourseSessionID { get; set; } public string Code { get; set; } [ConcurrencyCheck] public DateTime DateFrom { get; set; } [ConcurrencyCheck] public DateTime DateTo { get; set; } }
2. 同一批次重复记录引发的上下文状态冲突
如果文件中存在相同UserId+CourseId的多行记录,第一行被Add到上下文后处于Added状态;第二行通过GetRecord从上下文中获取该实体,调用Update会将其状态改为Modified。SaveChanges时EF会执行UPDATE语句,但此时数据库中尚无该记录,导致影响行数为0触发异常。
修复:先对文件记录按UserId+CourseId去重,保留最新的记录:
var uniqueFileRecords = fileRecords .GroupBy(r => new { r.UserId, r.CourseId }) .Select(g => g.OrderByDescending(r => DateTime.ParseExact(r.DateFrom, "yyyyMMdd", CultureInfo.InvariantCulture)).First()) .ToList(); foreach (var record in uniqueFileRecords) { // 原循环处理逻辑 }
3. 冗余的Update调用导致状态混乱
从上下文获取的existingRecord已被EF跟踪,修改属性后EF会自动记录变化,无需调用Update方法(Update用于附加未被跟踪的实体)。冗余调用会强制将实体状态设为Modified,干扰EF的自动跟踪逻辑。
修复:修改UpdateRecord方法,移除Update调用:
private void UpdateRecord(Data.Entities.Record existingRecord, FileRecord updatedRecord) { existingRecord.CourseSessionId = int.Parse(updatedRecord.CourseSessionId); existingRecord.DateFrom = string.IsNullOrWhiteSpace(updatedRecord.DateFrom) ? new DateTime(1900, 1, 1) // 替换为PostgreSQL支持的最小日期 : DateTime.ParseExact(updatedRecord.DateFrom, "yyyyMMdd", CultureInfo.InvariantCulture); existingRecord.DateTo = string.IsNullOrWhiteSpace(updatedRecord.DateTo) ? new DateTime(1900, 1, 1) : DateTime.ParseExact(updatedRecord.DateTo, "yyyyMMdd", CultureInfo.InvariantCulture); }
4. DateTime.MinValue与PostgreSQL日期兼容性问题
PostgreSQL的DATE类型不支持DateTime.MinValue(0001-01-01),如果将DateFrom/DateTo设为该值,插入操作会失败,导致记录无法写入数据库,后续更新自然找不到对应行。
修复:改用PostgreSQL支持的最小日期(如1900-01-01)替换DateTime.MinValue。
5. 开启EF日志排查SQL语句
通过日志查看EF生成的SQL语句,确认执行的是插入还是更新操作,以及WHERE条件是否正确,进一步定位问题:
// 在Startup.cs的DbContext配置中添加日志 builder.Services.AddDbContext<ESRContext>(options => options.UseNpgsql(Environment.GetEnvironmentVariable("PostgreDBConnectionString")) .LogTo(Console.WriteLine, LogLevel.Information));
内容的提问来源于stack exchange,提问作者JakeRoper

