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

EF6添加带Identity列的新条目时抛出乐观并发异常

问题原因说明

这个异常和实际的并发操作无关,是EF6的实体回查逻辑结合SQL Server DateTime精度特性共同导致的,具体原因如下:

  • 你的TestIdentity表使用ID + DTStamp作为复合主键,其中ID是Identity自增列,值由数据库生成。EF6插入该类型实体后,会自动执行回查逻辑:先执行INSERT语句插入数据,再用所有主键字段作为查询条件查询刚插入的行,把数据库生成的ID值回写到实体对象中,同时验证插入是否成功。
  • 回查的条件会被EF拼接为:WHERE ID = SCOPE_IDENTITY() AND DTStamp = @你传入的DTStamp参数
  • C#的DateTime.Now精度是100纳秒,而SQL Server的DATETIME类型精度只有3.33毫秒,你传入的DTStamp值存入数据库时会被自动截断精度。回查时用未截断的原始DateTime值去匹配数据库里截断后的值,自然匹配不到任何行,EF就会判定插入操作影响了0行,抛出你看到的乐观并发异常。
  • 你直接执行SQL语句可以插入成功,是因为不需要触发EF的回查和验证逻辑,只要SQL本身符合语法约束就能执行成功。
解决方案
  1. 最优方案:调整表结构,仅用ID作为唯一主键。Identity列本身已经可以唯一标识行,不需要将DTStamp加入主键,单独为DTStamp建立非聚集索引即可满足查询需求,从根源上避免该问题。
  2. 保留复合主键方案:将DTStamp字段的类型改为DATETIME2(7),该类型的精度和C#的DateTime完全匹配,不会出现精度截断问题,回查时可以正常匹配到行。也可以手动配置实体的FluentAPI,明确主键各字段的生成规则:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<TestIdentity>()
        .HasKey(t => new { t.ID, t.DTStamp });
    modelBuilder.Entity<TestIdentity>()
        .Property(t => t.ID)
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
  1. 临时兼容方案:如果不需要EF回写自增ID到实体,可以将ID属性的DatabaseGeneratedOption改为None,插入时手动给ID赋值,但会失去Identity列的自增特性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 21:24:03