我注意到当我使用EF插入时,它执行选择以找到下一个PK。
我有一个带有身份设置和启用自动增量的PK字段。
这是查询
SELECT [ackId]
FROM [dbo].[Acks]
WHERE @@ROWCOUNT > 0 AND [ackId] = scope_identity()
我碰巧注意到它,因为它位于SQL Manger Studio中最近的昂贵查询列表的顶部。找到PK的查询比实际插入更昂贵是没有意义的吗?
这是正常行为吗?或者这种行为是否由实体框架引起?
我能想到的另一个问题。如果EF正在执行select以获取值,那么如果有多个连接写入db会发生什么?当select返回相同的值时,是否会出现这种情况?
答案 0 :(得分:1)
是的,当插入带有身份密钥的新实体时,这是正常行为。
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
这是numeric和guid的默认约定
Code First推断,如果a上的属性属性是主键 class被命名为“ID”(不区分大小写),或者后面是类名 通过“ID”。如果主键属性的类型是数字或GUID它 将配置为标识列。 - MSDN
EF将通过选择最后一个标识值来更新插入密钥的临时密钥。
实体框架替换临时属性的值 key与数据源之后生成的标识值 SaveChanges被调用。 - MSDN
选择scope_identity
将返回插入实体的最后一个标识值,该值将是一个新的增量值。
如果您不希望每次插入新实体时都选择标识值,则可以禁用标识选项或使用流畅的api。
[DatabaseGenerated(DatabaseGeneratedOption.None)]
如果您插入大量记录并且不希望EF重新选择身份密钥,您可以编写正常的ADO.NET SQL查询,或者也可以尝试使用Bulk Insert
。
答案 1 :(得分:1)
这是每个 ORM中支持数据库生成的身份密钥的常见模式。身份是实体的关键概念。例如,两个具有相同名称的客户端仍然是两个不同的客户端。像ClientId
这样的代理键是区分它们的唯一方法。
ORM需要知道数据库中的这个代理键值,并且在插入数据时唯一明确地获取它的方法是直接查询scope_identity()
。
这永远不会导致竞争条件,因为当插入发生时,标识列总是递增(它永远不会回滚),scope_identity()
总是返回在INSERT
语句范围内生成的标识值
摆脱这种昂贵模式的唯一方法是在代码中生成键值并将主键属性设置为DatabaseGeneratedOption.None
。但是,生成和插入没有并发问题的唯一主键值并非易事。
我想这是你必须要忍受的事情。 ORM从未打算进行批量插入,还有其他方法可以做到这些。