【问题标题】:Force EntityFramework to use OUTPUT not SCOPE_IDENTITY to retrieve key value强制 EntityFramework 使用 OUTPUT 而不是 SCOPE_IDENTITY 来检索键值
【发布时间】:2014-06-04 19:33:01
【问题描述】:

我首先使用实体​​框架代码来访问一组表,其键将由默认约束中基于 int 的 SEQUENCE 设置。 EF 似乎无法处理这个问题,它坚持在插入后使用 SCOPE_IDENTITY 来填充整数键字段。

深入研究代码,它看起来有点硬编码:

http://entityframework.codeplex.com/SourceControl/latest#src/EntityFramework.SqlServer/SqlGen/DmlSqlGenerator.cs

在页面的一半多一点处查看 IsValidScopeIdentityColumnType 方法。如果此方法返回 true,则使用 SCOPE_IDENTITY() 检索插入的 Key 值,否则生成 OUTPUT 子句。 (Guid/uniqueidentifier 是那里的典型用例)。

        // make sure it's a primitive type
        if (typeUsage.EdmType.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType)
        {
            return false;
        }

        // check if this is a supported primitive type (compare by name)
        var typeName = typeUsage.EdmType.Name;

        // integer types
        if (typeName == "tinyint"
            || typeName == "smallint"
            ||
            typeName == "int"
            || typeName == "bigint")
        {
            return true;
        }

有没有什么办法可以欺骗这个方法为一个整数字段返回 false ?一旦我开始看到诸如“EDMType”之类的东西,我就无法真正理解 EF 映射是如何工作的。也许有一些方法可以使用用户定义的类型来欺骗它?但确实是 .NET 端的配置需要某种更新。

另请参阅同一文件中的 UseGeneratedValuesVariable 方法以了解其使用位置...

我不清楚为什么这里不只是全面使用 OUTPUT —— 也许是性能?

【问题讨论】:

  • “我不清楚为什么这里不全面使用 OUTPUT”——正如在 UseGeneratedValuesVariable 方法中的 cmets 中所述,在您找到的同一个文件中,OUTPUT 没有'对于旧版本的 SQL Server 不存在。此外,即使使用较新的版本,我认为在某些情况下 OUTPUT 不起作用(与触发器有关)。
  • 他们实际上在某些地方检查了 SQL 的版本......但是触发器......总是一个问题

标签: c# entity-framework


【解决方案1】:

更新 - 仅支持 DB 生成的 PK 的身份

您可以创建一个标记为已计算的键列,该列具有DataseGeneratedOption.Computed。 (见DataseGeneratedOption enum)。

要表明这一点,您可以在 DbContext 的 OnModelCreating 方法中使用 DatabaseGeneratedAttribute 装饰列,或使用流畅的 API,如下所示:

        modelBuilder.Entity<EntityType>()
            .Property(c => c.KeyColumn)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)

此示例代码与 EF6.1 完美配合

public class MyDbContext : DbContext
{
    public IDbSet<ComputedKey> ComputedKeys { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        // Computed Key:
        modelBuilder.Entity<ComputedKey>()
            .HasKey(c => c.Id)
            .Property(c => c.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
    }
}

public class ComputedKey
{
    public int Id { get; set; }
}

当您运行应用程序时,表已正确创建。

当您尝试将第一个实体添加到实体集合并保存更改时,就会出现问题。你得到:Modifications to tables where a primary key column has property 'StoreGeneratedPattern' set to 'Computed' are not supported. Use 'Identity' pattern instead. Key column: 'Id'. Table: 'CodeFirstDatabaseSchema.ComputedKey'

这是 EF(直到 6.1)的一个限制,它只允许将整数类型或 GUID 作为 PK 的 DB 生成值。

解决方法

第一: 一种是使用在 DB 上生成的列作为替代键。

从 EF 6.1 开始,您可以创建 AK,使用如下属性装饰 AK 列:

[Index("MyIndex", unique: true)]

第二: 使用序列的原因是定义种子和增量。如果这是您需要的,您可以像这样修改身份:

DBCC CHECKIDENT ('SchemaName.TableName', RESEED, 10);

这意味着下一个生成的标识值将是11,增量为1。

如果您需要使用不同的增量,则需要删除并重新创建标识列,以指示种子和增量。但为此,您还需要删除并创建关联的 foreingk 键,因此实现起来太难了。

第三: 你可以使用触发器。在触发器中,您可以使用SET IDENTITY_INSERT tableName ON/OFF,但同样会遇到问题,因为@@identity 会不匹配。

注意:如果您需要运行自定义 SQL 命令来应用此自定义,您需要实现数据库初始化程序的 Seed 方法

结论

仅部分支持此方案,因此您宁愿找到替代解决方案,除非之前的解决方法之一适合您。

请求此功能

如果您对此功能感兴趣,请转至实体框架功能建议,并为此投票:Allow using SQL Server 2012 sequence for generating primary key

【讨论】:

  • 关键字段不能有“计算”元数据。引发异常。
  • 您必须发现在您的特定情况下会发生这种情况的原因。我有一个完美的概念证明。我已将代码复制到我的答案中。
  • 将您的代码添加到一个空项目中:{“不支持对主键列的属性 'StoreGeneratedPattern' 设置为 'Computed' 的表进行修改。改用 'Identity' 模式。键列: 'Id'。表:'CodeFirstDatabaseSchema.ComputedKey'。"}
  • 您需要实际尝试并保存其中一个实体才能触发尝试生成 SQL 的代码
  • 我正在使用 nuget 的 EF 6.1.0,如果这与您相关的话
猜你喜欢
  • 2020-11-29
  • 2011-03-02
  • 1970-01-01
  • 2012-01-29
  • 1970-01-01
  • 1970-01-01
  • 2020-08-23
  • 1970-01-01
  • 2012-03-04
相关资源
最近更新 更多