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

Cly*_*yde 6 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;
        }
Run Code Online (Sandbox Code Playgroud)

有没有办法欺骗这个方法为整数字段返回false?一旦我开始看到像'EDMType'这样的东西,我就超出了我对EF映射真正起作用的真正理解.也许有一些方法可以使用用户定义的类型来欺骗它?但它确实是.NET方面的配置需要某种更新.

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

我不清楚为什么OUTPUT不仅仅是全面使用 - 也许是性能?

Jot*_*aBe 5

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

您可以创建一个标记为计算的键列,其中包含一个DataseGeneratedOption.Computed.(请参阅DataseGeneratedOption枚举).

为了表明这一点,你可以使用DatabaseGeneratedAttribute来装饰列,或者在OnModelCreatingDbContext 的方法中使用流畅的API ,如下所示:

        modelBuilder.Entity<EntityType>()
            .Property(c => c.KeyColumn)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)
Run Code Online (Sandbox Code Playgroud)

此示例代码与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; }
}
Run Code Online (Sandbox Code Playgroud)

运行应用程序时,表格已正确创建.

当您尝试将第一个实体添加到实体集合并保存更改时,会出现问题.你得到: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)]
Run Code Online (Sandbox Code Playgroud)

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

DBCC CHECKIDENT ('SchemaName.TableName', RESEED, 10);
Run Code Online (Sandbox Code Playgroud)

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

如果需要使用不同的增量,则需要删除并重新创建标识列,指示种子和增量.但是为了做到这一点,你还需要删除并创建相关的foreingk键,因此实现起来太难了.

第三: 你可以使用触发器.你可以使用触发器内部SET IDENTITY_INSERT tableName ON/OFF,但是你可能会因为@@identity不匹配而遇到问题.

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

结论

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

请求这个功能

如果您对此功能感兴趣,请转到实体框架功能建议,并为此投票:允许使用SQL Server 2012序列生成主键