【问题标题】:EF performing a select on every insert with Identity columnEF 在每个带有标识列的插入上执行选择
【发布时间】:2014-08-26 07:47:58
【问题描述】:

我注意到,当我使用 EF 插入时,它会执行选择以查找下一个 PK。

我有一个设置了身份并启用了自动增量的 PK 字段。

这里是查询

SELECT [ackId]
FROM [dbo].[Acks]
WHERE @@ROWCOUNT > 0 AND [ackId] = scope_identity()

我碰巧注意到它,因为它位于 SQL Manger Studio 中最近昂贵的查询列表的顶部。查找 PK 的查询比实际插入的成本更高,这不是很有意义吗?

这是正常行为吗?或者这种行为是由实体框架引起的?

我能想到的另一个问题。如果 EF 正在执行选择以获取值,如果有多个连接写入数据库会发生什么?不能有select返回相同值的情况吗?

【问题讨论】:

    标签: sql sql-server entity-framework insert entity-framework-6.1


    【解决方案1】:

    是的,当插入带有身份密钥的新实体时,这是正常行为。

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    

    这是数字和 guid 的默认约定

    Code First 推断一个属性是主键,如果一个属性位于 类被命名为“ID”(不区分大小写),或后面的类名 通过“身份证”。如果主键属性的类型是数字或 GUID 它 将被配置为标识列。 - MSDN

    EF 将通过选择最后一个标识值来使用插入的密钥更新临时密钥。

    实体框架将属性的值替换为临时 具有由数据源生成的标识值的键 调用 SaveChanges。 - MSDN

    选择scope_identity 将返回插入实体的最后一个标识值,这将是一个新的增量值。

    如果不想每次插入新实体时都选择标识值,可以禁用标识选项或使用fluent api。

    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    

    如果你插入很多记录并且不希望 EF 重新选择身份密钥,你可以编写一个普通的 ADO.NET sql 查询,或者你也可以尝试使用Bulk Insert

    【讨论】:

      【解决方案2】:

      这是支持数据库生成的身份密钥的每个 ORM 中的常见模式。身份是实体的一个关键概念。例如,两个同名的客户端仍然是两个不同的客户端。像ClientId 这样的代理键是区分它们的唯一方法。

      ORM 需要知道数据库中的这个代理键值,并且在插入数据时明确获取它的唯一方法是直接查询scope_identity()

      这永远不会导致竞争条件,因为当插入发生时标识列总是递增(它从不回滚)并且scope_identity() 总是返回在INSERT 语句范围内生成的标识值。

      摆脱这种昂贵模式的唯一方法是在代码中生成键值并将主键属性设置为DatabaseGeneratedOption.None。但是在没有并发问题的情况下生成和插入唯一的主键值并非易事。

      我想这是你必须忍受的东西。 ORM 从来没有打算进行批量插入,还有其他方法可以做到这一点。

      【讨论】:

        猜你喜欢
        • 2011-08-03
        • 1970-01-01
        • 2019-06-14
        • 2014-02-16
        • 2011-10-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多