EF Core FOREIGN KEY 约束 > 多个级联路径
EF Core FOREIGN KEY constraint > multiple cascade paths
我试图在我的模型中实现以下关系(简化)。也就是说,每个Employee
可以有多个银行Accounts
(私人账户、公司账户等...)。每个员工都可以带任何与公司有关的Receipt
(例如煤气单、警察罚款等)。现在,根据这张收据的总价值,公司可以决定他们是否会立即向用户收取这个价值,或者,如果这笔钱很大,他们需要每月扣除(因此每张收据可以有多个 Transactions
在本文中被理解为扣除)。如果 Receipt
立即收费(参见模型上的 ReceiptType
属性),则此实体仅存在一个 Transaction
。最后(特别是在长期扣除的情况下),Transaction
需要访问 Account
,因为我们需要指定,这个值将记入哪个员工的账户。
这是我指定实体的方式:
public class EmployeeDB
{
public long Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public virtual IEnumerable<EmployeeAccountDB> Accounts { get; set; }
public virtual IEnumerable<ReceiptDB> Receipts { get; set; }
}
public class EmployeeAccountDB
{
public long Id { get; set; }
public long EmployeeId { get; set; }
public virtual EmployeeDB Employee { get; set; }
public virtual IEnumerable<TransactionDB> Transactions { get; set; }
}
public class ReceiptDB
{
public long Id { get; set; }
public long EmployeeDBId { get; set; }
public ReceiptTypeEnum ReceiptType { get; set; }
public double Value { get; set; }
public double VAT { get; set; }
public double TotalValue { get; set; }
public virtual EmployeeDB Employee { get; set; }
public virtual IEnumerable<TransactionDB> Transactions { get; set; }
}
public class TransactionDB
{
public long Id { get; set; }
public long EmployeeAccountId { get; set; }
public long ReceiptDBId { get; set; }
public virtual ReceiptDB Receipt { get; set; }
public virtual EmployeeAccountDB Account { get; set; }
}
现在通过 Fluent API 我指定了以下实体配置:
public class EmployeeDBConfiguration : IEntityTypeConfiguration<EmployeeDB>
{
public void Configure(EntityTypeBuilder<EmployeeDB> builder)
{
builder.ToTable("Employees", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.HireDate).IsRequired();
builder.HasMany(x => x.Accounts).WithOne(x => x.Employee).OnDelete(DeleteBehavior.Cascade);
builder.HasMany(x => x.Receipts).WithOne(x => x.Employee).OnDelete(DeleteBehavior.Cascade);
}
}
public class EmployeeAccountDBConfiguration : IEntityTypeConfiguration<EmployeeAccountDB>
{
public void Configure(EntityTypeBuilder<EmployeeAccountDB> builder)
{
builder.ToTable("EmployeeAccounts", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).ValueGeneratedOnAdd();
builder.HasOne(x => x.Employee).WithMany(o => o.Accounts).HasForeignKey(nameof(EmployeeAccountDB.EmployeeId));
builder.HasMany(x => x.Transactions).WithOne(x => x.Account).OnDelete(DeleteBehavior.Cascade);
}
}
public class ReceiptDBConfiguration : IEntityTypeConfiguration<ReceiptDB>
{
public void Configure(EntityTypeBuilder<ReceiptDB> builder)
{
builder.ToTable("Receipts", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).ValueGeneratedOnAdd();
builder.Property(t => t.TotalValue).IsRequired();
builder.HasOne(x => x.Employee).WithMany(x=>x.Receipts).HasForeignKey(x => x.EmployeeDBId);
builder.HasMany(x => x.Transactions).WithOne(x => x.Receipt).OnDelete(DeleteBehavior.Cascade);
}
}
class TransactionDBConfiguration : IEntityTypeConfiguration<TransactionDB>
{
public void Configure(EntityTypeBuilder<TransactionDB> builder)
{
builder.ToTable("Transactions", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).ValueGeneratedOnAdd();
}
}
迁移创建得很好。然而,当我尝试通过 dotnet ef database update
创建数据库时,问题出现了。我遇到以下问题
Introducing FOREIGN KEY constraint
'FK_Transactions_Receipts_ReceiptDBId' on table 'Transactions' may
cause cycles or multiple cascade path
我不太确定实际问题是什么。我的目标是(我试图设置的)当 Employee
被删除时,与该实体相关的所有后续实体 Account
、Receipt
、Transaction
将被删除以及(在配置 > 级联删除中指定)。
如能提供有关此事的任何帮助,我们将不胜感激。如果我的模型结构不正确,我很乐意重构它以保持它的简单和干净。
P.S。我正在连接到 SQL 数据库。
我收到了很多关于 SO 的好文章,它们正好解决了我的问题,但最后我决定如何解决它如下:
对于 Account
我决定实现软删除功能(这会导致帐户永远不会被删除,除非 Employee
被删除)因此我能够丢弃员工 > 帐户 > 交易路径如下(最后一行代码):
public class EmployeeAccountDBConfiguration : IEntityTypeConfiguration<EmployeeAccountDB>
{
public void Configure(EntityTypeBuilder<EmployeeAccountDB> builder)
{
builder.ToTable("EmployeeAccounts", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).ValueGeneratedOnAdd();
builder.HasOne(x => x.Employee).WithMany(o => o.Accounts).HasForeignKey(nameof(EmployeeAccountDB.EmployeeId));
builder.HasMany(x => x.Transactions).WithOne(x => x.Account).OnDelete(DeleteBehavior.NoAction);
}
}
这样 SQL 服务器在模型中只看到 1 个正确的级联路径(员工 > 收据 > 交易),效果很好。
我试图在我的模型中实现以下关系(简化)。也就是说,每个Employee
可以有多个银行Accounts
(私人账户、公司账户等...)。每个员工都可以带任何与公司有关的Receipt
(例如煤气单、警察罚款等)。现在,根据这张收据的总价值,公司可以决定他们是否会立即向用户收取这个价值,或者,如果这笔钱很大,他们需要每月扣除(因此每张收据可以有多个 Transactions
在本文中被理解为扣除)。如果 Receipt
立即收费(参见模型上的 ReceiptType
属性),则此实体仅存在一个 Transaction
。最后(特别是在长期扣除的情况下),Transaction
需要访问 Account
,因为我们需要指定,这个值将记入哪个员工的账户。
这是我指定实体的方式:
public class EmployeeDB
{
public long Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public virtual IEnumerable<EmployeeAccountDB> Accounts { get; set; }
public virtual IEnumerable<ReceiptDB> Receipts { get; set; }
}
public class EmployeeAccountDB
{
public long Id { get; set; }
public long EmployeeId { get; set; }
public virtual EmployeeDB Employee { get; set; }
public virtual IEnumerable<TransactionDB> Transactions { get; set; }
}
public class ReceiptDB
{
public long Id { get; set; }
public long EmployeeDBId { get; set; }
public ReceiptTypeEnum ReceiptType { get; set; }
public double Value { get; set; }
public double VAT { get; set; }
public double TotalValue { get; set; }
public virtual EmployeeDB Employee { get; set; }
public virtual IEnumerable<TransactionDB> Transactions { get; set; }
}
public class TransactionDB
{
public long Id { get; set; }
public long EmployeeAccountId { get; set; }
public long ReceiptDBId { get; set; }
public virtual ReceiptDB Receipt { get; set; }
public virtual EmployeeAccountDB Account { get; set; }
}
现在通过 Fluent API 我指定了以下实体配置:
public class EmployeeDBConfiguration : IEntityTypeConfiguration<EmployeeDB>
{
public void Configure(EntityTypeBuilder<EmployeeDB> builder)
{
builder.ToTable("Employees", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.HireDate).IsRequired();
builder.HasMany(x => x.Accounts).WithOne(x => x.Employee).OnDelete(DeleteBehavior.Cascade);
builder.HasMany(x => x.Receipts).WithOne(x => x.Employee).OnDelete(DeleteBehavior.Cascade);
}
}
public class EmployeeAccountDBConfiguration : IEntityTypeConfiguration<EmployeeAccountDB>
{
public void Configure(EntityTypeBuilder<EmployeeAccountDB> builder)
{
builder.ToTable("EmployeeAccounts", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).ValueGeneratedOnAdd();
builder.HasOne(x => x.Employee).WithMany(o => o.Accounts).HasForeignKey(nameof(EmployeeAccountDB.EmployeeId));
builder.HasMany(x => x.Transactions).WithOne(x => x.Account).OnDelete(DeleteBehavior.Cascade);
}
}
public class ReceiptDBConfiguration : IEntityTypeConfiguration<ReceiptDB>
{
public void Configure(EntityTypeBuilder<ReceiptDB> builder)
{
builder.ToTable("Receipts", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).ValueGeneratedOnAdd();
builder.Property(t => t.TotalValue).IsRequired();
builder.HasOne(x => x.Employee).WithMany(x=>x.Receipts).HasForeignKey(x => x.EmployeeDBId);
builder.HasMany(x => x.Transactions).WithOne(x => x.Receipt).OnDelete(DeleteBehavior.Cascade);
}
}
class TransactionDBConfiguration : IEntityTypeConfiguration<TransactionDB>
{
public void Configure(EntityTypeBuilder<TransactionDB> builder)
{
builder.ToTable("Transactions", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).ValueGeneratedOnAdd();
}
}
迁移创建得很好。然而,当我尝试通过 dotnet ef database update
创建数据库时,问题出现了。我遇到以下问题
Introducing FOREIGN KEY constraint 'FK_Transactions_Receipts_ReceiptDBId' on table 'Transactions' may cause cycles or multiple cascade path
我不太确定实际问题是什么。我的目标是(我试图设置的)当 Employee
被删除时,与该实体相关的所有后续实体 Account
、Receipt
、Transaction
将被删除以及(在配置 > 级联删除中指定)。
如能提供有关此事的任何帮助,我们将不胜感激。如果我的模型结构不正确,我很乐意重构它以保持它的简单和干净。
P.S。我正在连接到 SQL 数据库。
我收到了很多关于 SO 的好文章,它们正好解决了我的问题,但最后我决定如何解决它如下:
对于 Account
我决定实现软删除功能(这会导致帐户永远不会被删除,除非 Employee
被删除)因此我能够丢弃员工 > 帐户 > 交易路径如下(最后一行代码):
public class EmployeeAccountDBConfiguration : IEntityTypeConfiguration<EmployeeAccountDB>
{
public void Configure(EntityTypeBuilder<EmployeeAccountDB> builder)
{
builder.ToTable("EmployeeAccounts", ApplicationDbContext.DefaultSchema);
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).ValueGeneratedOnAdd();
builder.HasOne(x => x.Employee).WithMany(o => o.Accounts).HasForeignKey(nameof(EmployeeAccountDB.EmployeeId));
builder.HasMany(x => x.Transactions).WithOne(x => x.Account).OnDelete(DeleteBehavior.NoAction);
}
}
这样 SQL 服务器在模型中只看到 1 个正确的级联路径(员工 > 收据 > 交易),效果很好。