c# - 强制 EntityFramework 使用 OUTPUT 而不是 SCOPE_IDENTITY 来检索键值

标签 c# entity-framework

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

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

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

在页面的中间位置查看 IsValidScopeIdentityColumnType 方法。如果此方法返回 true,则使用 SCOPE_IDENTITY() 检索插入的键值,否则生成 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——也许是性能?

最佳答案

更新 - 数据库生成的 PK 仅支持身份

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

为了表明这一点,您可以用 DatabaseGeneratedAttribute 修饰该列,或在 DbContext 的 OnModelCreating 方法中使用 Fluent 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; }
}

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

当您尝试将第一个实体添加到实体集合并保存更改时,就会出现问题。您得到: 不支持对主键列的属性“StoreGeneratedPattern”设置为“Computed”的表进行修改。请改用“身份”模式。关键列:'Id'。表:“CodeFirstDatabaseSchema.ComputedKey”

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

变通办法

首先: 一种是使用在数据库上生成的列作为替代键。

从 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 方法

结论

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

请求此功能

如果您对此功能感兴趣,请转到 Entity Framework 功能建议,并为此投票:Allow using SQL Server 2012 sequence for generating primary key

关于c# - 强制 EntityFramework 使用 OUTPUT 而不是 SCOPE_IDENTITY 来检索键值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23962690/

相关文章:

c# - 值不能为空。参数名称 : source

c# - 无法使用 JRE 7u51 在 Internet Explorer 11 中运行 Java 小程序

c# - 使用 sqlite 的 Entity Framework Core 中的奇怪行为

c# - LINQ让重复数据

c# - 集合的共享主键关联

c# - ESQL 不工作

c# - 使用 Description 属性装饰 EF 对象模型上的枚举?

c# - 如何在 Entity Framework 中的 DbSet 属性上设置过滤选项?

c# - "The operation cannot be completed because the DbContext has been disposed"

c# - Entity Framework 如何仅更新模型中的特定字段?