TPC - 身份主键

TPC - Identity primary key

我首先使用代码 (EntityFramework 6.1) 和 Table 每个具体类型的方法。

根据这个 thread 的最后一条评论,EF 6.1 没有设置 SQL 自动递增 Identity on。我明白了。

但我想在上强制使用Identity ,因为我永远不会使用(也不会定义)基础class DbSet<>。我不会访问基础 class 集合,只会访问具体的集合。

更多的文字,一些代码:

public abstract class BaseModel
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] // <- tried to force it, but does not work
    public long Id { get; set; }

    [Timestamp]
    public byte[] DataVersion { get; set; }

    public string CreatedWho { get; set; }
    public DateTime CreatedWhen { get; set; }
    public string UpdatedWho { get; set; }
    public DateTime UpdatedWhen { get; set; }
}

public class Currency : BaseModel
{
    public string IsoCode {get; set; }
}

在我的 DbContext class 中有:

public DbSet<Currency> Currencies {get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Enable TPC Inheritance
    modelBuilder.Entity<Currency>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable(typeof(Currency).Name);
        });

    base.OnModelCreating(modelBuilder);
}

使用此代码,在我生成的数据库 Table Currency 中,我拥有预期的所有 BaseModel 列,Id 是主键,好的。但是它没有 Identity 属性 设置 on,尽管使用了明确的 DatabaseGenerated 属性。

如何在 TPC 继承架构中强制 Identity

标识列是每个 table,使用 TPC 你有一个 table 每个 class,并且没有共享基础 class table身份生成的 id。如果您有具体类型 table 生成标识作为 id,您将在这些 table 中获得重复的 id。

可以找到关于此问题的讨论,例如here,这部分涵盖了这个具体问题:

How to Solve The Identity Problem in TPC

As you saw, using SQL Server’s int identity columns doesn't work very well together with TPC since there will be duplicate entity keys when inserting in subclasses tables with all having the same identity seed. Therefore, to solve this, either a spread seed (where each table has its own initial seed value) will be needed, or a mechanism other than SQL Server’s int identity should be used. Some other RDBMSes have other mechanisms allowing a sequence (identity) to be shared by multiple tables, and something similar can be achieved with GUID keys in SQL Server. While using GUID keys, or int identity keys with different starting seeds will solve the problem but yet another solution would be to completely switch off identity on the primary key property. As a result, we need to take the responsibility of providing unique keys when inserting records to the database. We will go with this solution since it works regardless of which database engine is used.

看来 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 终于可以在 TPC 继承上下文中强制我的 PK 上的身份行为。

诀窍?程序包控制台命令 Update-Databse -Force 不会 自动应用此类更改。您需要删除您的表格以重新创建它们。

我使用以下 SQL 脚本删除所有内容(甚至 __MigrationHistory),灵感来自 this blog

WHILE ( EXISTS ( SELECT 1
                 FROM   INFORMATION_SCHEMA.TABLE_CONSTRAINTS
                 WHERE  CONSTRAINT_TYPE = 'FOREIGN KEY' ) )
    BEGIN
        DECLARE @sql NVARCHAR(2000)
        SELECT TOP 1
                @sql = ( 'ALTER TABLE ' + TABLE_SCHEMA + '.[' + TABLE_NAME
                         + '] DROP CONSTRAINT [' + CONSTRAINT_NAME + ']' )
        FROM    INFORMATION_SCHEMA.TABLE_CONSTRAINTS
        WHERE   CONSTRAINT_TYPE = 'FOREIGN KEY'
        EXEC (@sql)
        PRINT @sql
    END
GO 

WHILE ( EXISTS ( SELECT 1
                 FROM   INFORMATION_SCHEMA.TABLES ) )
    BEGIN
        DECLARE @sql NVARCHAR(2000)
        SELECT TOP 1
                @sql = ( 'DROP TABLE ' + TABLE_SCHEMA + '.[' + TABLE_NAME
                         + ']' )
        FROM    INFORMATION_SCHEMA.TABLES

        EXEC (@sql)
        PRINT @sql
    END

之后,重新运行 Update-Database -Force 命令重新生成所有表。 Identity 将被激活。