Force EntityFramework使用OUTPUT而不是SCOPE_IDENTITY来检索键值

时间:2014-05-30 20:12:27

标签: c# entity-framework

我首先使用Entity Framework代码来访问一组表,这些表的键将由默认约束中的基于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' EDMType'我超越了我对EF映射真正起作用的真正理解。也许有某种方法可以使用用户定义的类型来欺骗它?但它确实是.NET方面的配置需要某种更新。

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

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

1 个答案:

答案 0 :(得分:5)

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

您可以创建标记为计算的键列,其中包含DataseGeneratedOption.Computed。 (见DataseGeneratedOption enum)。

为了表明这一点,您可以使用DatabaseGeneratedAttribute装饰列,或者使用DbContext的OnModelCreating方法中的流畅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生成值。

<强> WORKAROUNDS

<强>首先: 一种方法是使用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命令来应用此自定义,则需要实现db初始化程序的Seed方法

<强>结论

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

请求此功能

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