EF Core: как каскадно удалять, когда DB-first FK работает неправильно? - PullRequest
0 голосов
/ 08 февраля 2019

Рассмотрим эту (предположительно довольно запутанную) модель сущности унаследованной базы данных:

enter image description here

Как вы можете видеть, модели Blog s имеютколлекция Entry с указанием на блог Post каждый.Кончики стрелок указывают на принципала / родителя отношений.По крайней мере, так выглядит ядро ​​EF в зависимости от того, где хранятся внешние ключи.

К сожалению, FK между Entry и Post неправильный, поскольку Post на самом делеребенок Entry;то есть, когда я удаляю Entry из Blog, следует также удалить связанный Post.

Поскольку я не могу изменить схему устаревшей базы данных, мне интересно, какЯ могу настроить EF для каскадного удаления Post s, хотя он не считает их потомками Entry s.

Я попытался явно указать 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; }
}

I 'В настоящее время я проверяю, могут ли помочь принадлежащие типу сущности , но я скептически отношусь.

Полагаю, мне нужен способ явно указать родителя / принципала отношения сущности.Что-то вроде SetPrincipalEntityType().

Конечно, я всегда могу вручную удалить Post, но я пытаюсь избежать этого.

Есть идеи у кого-нибудь?

Рабочий пример

Я создал тестовый проект с интеграционными тестами, которые можно использовать для воспроизведения проблемы:

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

Тест DeletesTextWhenDeletingReview завершится ошибкой.

1 Ответ

0 голосов
/ 16 февраля 2019

Я разместил тот же вопрос на странице EF Core GitHub.Идея решения , опубликованного там , состоит в том, чтобы найти потерянные сущности с переопределением 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);
}

Я взял это и построил решение на основе атрибутов изэто вы можете найти в репозитории тестового проекта .

...