EF Core SQLite TPH interitance组合键->重复键?
EF Core SQLite TPH interitance combined key -> duplicate key?
我正在尝试使用一对多关系和 TPH 映射设置一个简单的 SQLite 数据库。
Table 已使用以下命令应用创建:
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
);
模型 classes 定义为:
[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.
???
数据库中的所有 table 都是空的(新创建的)。
我的猜测是 discriminator
值在将主值添加到上下文时尚未确定。添加一个包含 discriminiator
值的字段不会解决问题(我也猜到了,但试了一下...)。
我真的必须向从站添加一个专用的 id 列吗 table 以使其与 ef-core 一起工作,或者是否有任何其他解决方案?
基于 SBFrancies 提示的解决方案
向基 class Slave
添加一个名为 Discriminator
:
的字段
[Column("Discriminator")]
[Required]
public abstract string Discriminator { get; set; }
更改SlaveA
和SlaveB
的实现:
public override string Discriminator
{
get => nameof(SlaveA);
set { }
}
这两个从属具有相同的 MasterID ("Master 01") 和相同的鉴别器 (null),因此它们具有相同的主键,这就是导致问题的原因。
更新
一个可能的解决方案是为每种类型添加一个鉴别器:
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 table.
上有一个新的人工主键
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);
我正在尝试使用一对多关系和 TPH 映射设置一个简单的 SQLite 数据库。
Table 已使用以下命令应用创建:
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
);
模型 classes 定义为:
[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.
???
数据库中的所有 table 都是空的(新创建的)。
我的猜测是 discriminator
值在将主值添加到上下文时尚未确定。添加一个包含 discriminiator
值的字段不会解决问题(我也猜到了,但试了一下...)。
我真的必须向从站添加一个专用的 id 列吗 table 以使其与 ef-core 一起工作,或者是否有任何其他解决方案?
基于 SBFrancies 提示的解决方案
向基 class Slave
添加一个名为 Discriminator
:
[Column("Discriminator")]
[Required]
public abstract string Discriminator { get; set; }
更改SlaveA
和SlaveB
的实现:
public override string Discriminator
{
get => nameof(SlaveA);
set { }
}
这两个从属具有相同的 MasterID ("Master 01") 和相同的鉴别器 (null),因此它们具有相同的主键,这就是导致问题的原因。
更新
一个可能的解决方案是为每种类型添加一个鉴别器:
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 table.
上有一个新的人工主键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);