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

EF 6数据库优先模式下,如何正确添加含复合主键的实体?

EF6数据库优先模式下复合主键实体插入异常问题

错误信息

System.Data.Entity.Infrastructure.DbUpdateConcurrencyException: 'Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded.'

环境配置

  • 采用EF6数据库优先(DB First)模式
  • 表的复合主键由两列组成:
    • RecordId:int类型自增列(identity)
    • Created:datetime类型
  • 两列均通过EDMX设置为实体键

示例代码

Dim ctx = New TestEntities
Dim newTestRecord = New TestTable
newTestRecord.Created = Now
newTestRecord.DataPoint = "this is a test"
ctx.TestTables.Add(newTestRecord)
ctx.SaveChanges()

调用ctx.SaveChanges()时抛出上述异常。

已尝试的操作与观察

  • 将RecordId设为单一主键,刷新EDMX后插入操作正常
  • 原代码可与数据库交互:即使抛出异常,自增列RecordId仍会递增;若Created设为SQL datetime不合法值会报错
  • 将Created设为单一主键,刷新EDMX后插入操作正常

核心疑问

  1. 添加含复合主键的实体是否需要特殊操作?
  2. 这种包含代码设置列与SQL Server自增列的复合主键方案是否可行?

补充背景:使用复合主键是因为需要对底层表进行分区,Created字段需作为聚集索引的一部分用于分区函数/方案。


问题原因与解决方案

1. 异常根源

EF6处理包含自增列的复合主键时,插入后会尝试用完整主键(RecordId+Created)匹配数据库行来验证插入结果。问题出在:

  • 客户端设置的Created = Now是本地高精度时间,而SQL Server的datetime类型精度仅为3.33ms,两者存储值存在精度差异
  • 插入后EF用客户端缓存的Created值+数据库生成的RecordId查询匹配行,因精度差异查不到对应记录,就会抛出“影响0行”的并发异常

2. 复合主键方案的可行性

这种方案理论可行,但存在EF验证逻辑的坑,更推荐的替代方案是:

  • 保留RecordId作为单一自增主键,承担唯一标识行的核心作用
  • 单独创建包含Created字段的聚集索引用于分区,SQL Server允许聚集索引与主键分离,只需保证聚集索引键唯一(可添加唯一性约束)

主键的核心职责是唯一标识实体,分区需求无需绑定到主键上,分离设计更符合主键的本质作用。

3. 坚持使用复合主键的修复方法

若必须保留当前复合主键结构,可通过以下方式解决:

  • 调整EDMX字段配置:将Created字段的StoreGeneratedPattern设为None,明确告知EF该字段由客户端提供
  • 统一时间精度:将客户端生成的Created值截断到SQL Server datetime的精度(3.33ms),或改用datetime2类型(更高精度,减少差异)
  • 跳过EF行计数验证:重写上下文的SaveChanges方法,对该实体跳过插入后的行匹配验证,或手动将实体状态设为Added后直接提交

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 11:22:09