EF Core:当 DB-first FK 是错误的方式时如何级联删除?

EF Core: How to cascade delete when DB-first FK is the wrong way around?

考虑遗留数据库的这个(诚然相当混乱)实体模型:

如您所见,它对 Blog 建模,其中包含 Entry 的集合,每个指向一个博客 Post。箭头的尖端指向关系的principal/parent。至少这是 EF 核心根据存储外键的位置看到它的方式。

不幸的是,EntryPost 之间的 FK 是错误的方式,因为 Post 实际上是 Entry 的 child;即当我从 Blog 中删除 Entry 时,关联的 Post 也应该被删除。

由于我无法更改遗留数据库的模式,我想知道如何说服 EF 到 cascade-delete Posts,尽管它不认为它们是 children Entrys.

我尝试明确指定 DeleteBehavior 但无济于事:

modelBuilder.Entity<Entry>()
    .HasOne(e => e.Post)
    .WithOne(p => p.Entry)
    .OnDelete(DeleteBehavior.Cascade);

我还尝试使导航 属性 Post.Entry 成为必需,以指示 Post 不能没有 Entry,但这也无济于事:

public class Post {
    // ...
    [Required]
    [InverseProperty(nameof(Entry.Post))]
    public Entry Entry { get; set; }
}

我目前正在检查 owned entity types 是否有帮助,但我持怀疑态度。

我想我需要的是一种明确说明实体关系的 parent/principal 的方法。类似于 SetPrincipalEntityType().

当然我总是可以手动删除 Post,但我正在努力避免这种情况。

有什么想法吗?

工作示例

我创建了一个带有集成测试的 test project,您可以使用它来重现问题:

> git clone https://github.com/bert2/ef-vs-garbage-db.git
> cd .\ef-vs-garbage-db\
> dotnet test

测试 DeletesTextWhenDeletingReview 将失败。

我在 EF Core GitHub 页面上发布了同样的问题。 solution posted there 的想法是在 SaveChanges() 的覆盖中找到孤立的实体并手动删除它们:

public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
    // Note that this is internal code to force cascade deletes to happen.
    // It may stop working in any future release.
    ChangeTracker.DetectChanges();
    this.GetService<IStateManager>().GetEntriesToSave();

    try
    {
        ChangeTracker.AutoDetectChangesEnabled = false;

        foreach (var entry in ChangeTracker
            .Entries<CritiqueText>()
            .Where(e => Entry(e.Entity.Review).State == EntityState.Deleted))
        {
            entry.State = EntityState.Deleted;
        }
    }
    finally
    {
        ChangeTracker.AutoDetectChangesEnabled = true;
    }

    return base.SaveChanges(acceptAllChangesOnSuccess);
}

我利用它构建了一个基于属性的解决方案,您可以在 test project repo.

中找到它