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 被删除时,与该实体相关的所有后续实体 AccountReceiptTransaction 将被删除以及(在配置 > 级联删除中指定)。

如能提供有关此事的任何帮助,我们将不胜感激。如果我的模型结构不正确,我很乐意重构它以保持它的简单和干净。

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 个正确的级联路径(员工 > 收据 > 交易),效果很好。