EF Core SQLite TPH中断组合键->重复键?

时间:2018-07-23 13:00:12

标签: c# sqlite entity-framework-core ef-core-2.1

我正在尝试使用一对多关系和TPH映射设置一个简单的SQLite数据库。

已使用以下命令创建了表:

CREATE TABLE Masters (Id VARCHAR2 PRIMARY KEY);

CREATE TABLE Slaves (
    MasterId VARCHAR2 NOT NULL,
    Discriminator VARCHAR2 NOT NULL,
    SlaveAValue VARCHAR2,
    SlaveBValue VARCHAR2,
    PRIMARY KEY (MasterId, Discriminator),
    FOREIGN KEY (MasterId) REFERENCES Master(Id) ON DELETE CASCADE
);

模型类定义为:

[Table("Masters")]
public class Master
{
    [Column("Id")]
    [Key, Required]
    public string Id { get; set; }

    public List<Slave> Slaves { get; set; } = new List<Slave>();
}

[Table("Slaves")]
public abstract class Slave
{
    [Column("MasterId")]
    [Required]
    public string MasterId { get; set; }

    [ForeignKey("MasterId")]
    public Master Master { get; set; }
}

public class SlaveA : Slave
{
    [Column("SlaveAValue")]
    [Required]
    public string SlaveAValue { get; set; }
}

public class SlaveB : Slave
{
    [Column("SlaveBValue")]
    [Required]
    public string SlaveBValue { get; set; }
}

要告诉EF slaves具有组合键,我将覆盖内容OnModelCreating

modelbuilder.Entity<Slave>().HasKey("MasterId", "Discriminator");
base.OnModelCreating(modelbuilder);

但是,如果我尝试向上下文添加值,则会出现异常:

var master = new Master { Id = "Master 01" };
var slaveA = new SlaveA { Master = master, SlaveAValue = "A 01" };
var slaveB = new SlaveB { Master = master, SlaveBValue = "B 01" };

master.Slaves.Add(slaveA);
master.Slaves.Add(slaveB);

await context.Masters.AddAsync(master); // Exception
await context.SaveChangesAsync();

异常消息为:The instance of entity type 'SlaveB' cannot be tracked because another instance with the same key value for {'MasterId', 'Discriminator'} is already being tracked.

???

数据库中的所有表都完全为空(新创建)。

我的猜测是在将主值添加到上下文时尚未确定discriminator值。添加一个包含discriminiator值的字段将无法解决问题(我也猜到了,但是可以尝试一下...)。

我真的必须在从属表中添加专用的ID列以使其与ef-core一起使用吗?

基于SBFrancies的解决方案

向基类Slave添加一个名为Discriminator的字段:

[Column("Discriminator")]
[Required]
public abstract string Discriminator { get; set; }

更改SlaveASlaveB的实现:

public override string Discriminator
{
    get => nameof(SlaveA); 
    set { }
}

1 个答案:

答案 0 :(得分:0)

两个从站具有相同的MasterID(“主站01”)和相同的鉴别符(空),因此它们具有导致问题的相同主键。

更新

可能的解决方案是为每种类型添加一个鉴别器:

public class SlaveA : Slave
{
    public string Discriminator => "S1";

    [Column("SlaveAValue")]
    [Required]
    public string SlaveAValue { get; set; }
}

public class SlaveB : Slave
{
    public string Discriminator => "S2";

    [Column("SlaveBValue")]
    [Required]
    public string SlaveBValue { get; set; }
}

替代解决方案

以上的道歉。我认为更好的选择是在Slaves表上有一个新的人工主键。

CREATE TABLE Slaves (
    SlavesId INT NOT NULL IDENTITY PRIMARY KEY,
    MasterId VARCHAR2 NOT NULL,
    Discriminator VARCHAR2 NOT NULL,
    SlaveAValue VARCHAR2,
    SlaveBValue VARCHAR2,
    FOREIGN KEY (MasterId) REFERENCES Master(Id) ON DELETE CASCADE
);

[Table("Slaves")]
public abstract class Slave
{

    [Column("SlaveId")]
    [Key]
    public int SlaveId {get;set;}

    [Column("MasterId")]
    [Required]
    public string MasterId { get; set; }

    [ForeignKey("MasterId")]
    public Master Master { get; set; }
}

然后在数据库中添加一个唯一索引,以确保每个主数据库最多包含一种类型:

CREATE UNIQUE INDEX master_index ON Slaves(MasterId, Discriminator);