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

WPF应用使用EF Core 5操作SQLite新增实体时出现并发冲突问题

问题背景

我排查该问题的时间已经远超预期,且这是我首次将EF Core与SQLite搭配使用,目前仍未定位到问题根因。当前我开发的是基于.NET Core 3.1的单用户WPF应用,采用EF Core 5作为ORM框架,新增某一特定实体时触发报错,上下文内其余实体的新增、更新操作均无异常。

异常信息

新增实体时抛出的异常信息如下:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded.

实体类定义

public class SourceDataElement
{
    public int Id { get; set; }
    public int ColumnIndex { get; set; }
    // 外键关联到SourceDataRow
    public int RowId { get; set; }
    public SourceDataRow Row { get; set; }
    public string StringValue { get; set; }
    public int? IntegerValue { get; set; }
    public bool? BooleanValue { get; set; }
    public DateTime? DateTimeValue { get; set; }
    public Double? DoubleValue { get; set; }
    // 外键关联到SourceDataElementDataType
    public int SourceDataElementDataTypeId { get; set; }
    public SourceDataElementDataType SourceDataElementDataType { get; set; }
    public DateTime? LastUpdated { get; set; }
    [Timestamp] public byte[] Timestamp { get; set; }
}

前置排查结论

我专门搭建了小型测试程序单独验证该实体的操作逻辑,尝试了多种调整方案都没有解决问题。我可以手动向数据库对应表中插入数据,说明问题出在EF Core的处理逻辑上。

新增实体的业务代码

var dateTimeStamp = DateTime.Now;
var newSourceDataElement = new SourceDataElement()
{
    ColumnIndex = 0,
    RowId = 1,
    StringValue = "Test Data",
    IntegerValue = null,
    BooleanValue = null,
    DateTimeValue = null,
    DoubleValue = null,
    SourceDataElementDataTypeId = 1,
    LastUpdated = dateTimeStamp
};
_context.Add(newSourceDataElement);
await _context.SaveChangesAsync();

EF Core生成的执行SQL

我查看了EF Core生成的执行SQL,看起来逻辑没有问题:

dbug: 10/18/2021 10:41:45.714 RelationalEventId.CommandExecuting[20100] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executing DbCommand [Parameters=[@p0=NULL, @p1='0' (DbType = String), @p2=NULL, @p3=NULL, @p4=NULL, @p5='2021-10-18T10:41:45.4850016-04:00' (Nullable = true) (DbType = String), @p6='1' (DbType = String), @p7='1' (DbType = String), @p8='Test Data' (Size = 9)], CommandType='Text', CommandTimeout='30']
      INSERT INTO "SourceDataElements" ("BooleanValue", "ColumnIndex", "DateTimeValue", "DoubleValue", "IntegerValue", "LastUpdated", "RowId", "SourceDataElementDataTypeId", "StringValue")
      VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8);
      SELECT "Id", "Timestamp"
      FROM "SourceDataElements"
      WHERE changes() = 1 AND "rowid" = last_insert_rowid();
info: 10/18/2021 10:41:45.720 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executed DbCommand (8ms) [Parameters=[@p0=NULL, @p1='0' (DbType = String), @p2=NULL, @p3=NULL, @p4=NULL, @p5='2021-10-18T10:41:45.4850016-04:00' (Nullable = true) (DbType = String), @p6='1' (DbType = String), @p7='1' (DbType = String), @p8='Test Data' (Size = 9)], CommandType='Text', CommandTimeout='30']
      INSERT INTO "SourceDataElements" ("BooleanValue", "ColumnIndex", "DateTimeValue", "DoubleValue", "IntegerValue", "LastUpdated", "RowId", "SourceDataElementDataTypeId", "StringValue")
      VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8);
      SELECT "Id", "Timestamp"
      FROM "SourceDataElements"
      WHERE changes() = 1 AND "rowid" = last_insert_rowid();

已尝试的无效方案

  • 给LastUpdated列添加[Concurrency]特性
  • 新增插入和更新触发器自动维护timestamp字段

问题根因与解决方案

根因分析

问题出在你新增的维护Timestamp字段的触发器上:
EF Core完成插入操作后,会执行后续SELECT语句回查生成的Id和Timestamp值,WHERE条件中的changes()=1是判断插入操作是否成功。而你添加的触发器在插入完成后会自动更新Timestamp字段,导致SQLite的changes()函数返回的计数变为2,不满足changes()=1的条件,回查不到数据,EF Core就会判定插入操作影响行数为0,抛出并发异常。

可用解决方案

方案一(推荐):删除手动维护Timestamp的触发器

EF Core本身会在插入、更新实体时自动处理[Timestamp]标记的并发令牌字段的赋值,不需要额外通过触发器维护,直接删除你新增的插入、更新触发器即可解决问题。

方案二:在EF Core中配置数据库层面的Timestamp生成逻辑

如果你确实需要数据库层面维护Timestamp值,删除触发器后在DbContext的OnModelCreating方法中添加如下配置即可:

modelBuilder.Entity<SourceDataElement>()
    .Property(x => x.Timestamp)
    .IsRowVersion()
    .HasDefaultValueSql("CURRENT_TIMESTAMP")
    .ValueGeneratedOnAddOrUpdate();

方案三(临时验证用):移除并发令牌标记

暂时去掉Timestamp字段的[Timestamp]特性,如果插入操作恢复正常,即可100%确认是并发令牌的处理逻辑冲突导致的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 20:36:03