我正在尝试使用一对多关系和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; }
更改SlaveA
和SlaveB
的实现:
public override string Discriminator
{
get => nameof(SlaveA);
set { }
}
答案 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);