Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Q: How to cascade deletes when DB-first FK is the wrong way around? #14658

Closed
bert2 opened this issue Feb 9, 2019 · 3 comments
Closed

Q: How to cascade deletes when DB-first FK is the wrong way around? #14658

bert2 opened this issue Feb 9, 2019 · 3 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@bert2
Copy link

bert2 commented Feb 9, 2019

I'm currently working with a legacy DB where a one-to-one relation has been modeled the wrong way around; i.e. the foreign key column is stored on the entity which should be the principal entity of the relation. Consequently, EF is refusing to cascade-delete the should-be dependent entity of the relation.

TL;DR

Is there a way to specify the principal entity of a one-to-one relation independently of where the FK is stored?

Detailed explanation

Consider this model of a very similar schema:
image

Here a Post has a collection of PositiveReviews which each have a PraiseText (yes, I know, it's a stupid example, but I hope it gets my point across). As you can see the FK of the relation PositiveReview-PraiseText is stored on PositiveReview, effectively making PraiseText the principal entity of the relation. This means whenever a PositiveReview gets deleted from a Post, then the corresponding PraiseText will not be deleted and remains orphaned in the DB.

How can I convince EF to cascade-delete those PraiseTexts?

Steps to reproduce

I created a test project with integration tests that you can use to reproduce the issue:

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

The test DeletesTextWhenDeletingReview will fail.

Further details

EF Core version: 2.2.1
Database Provider: Microsoft.EntityFrameworkCore.Sqlite
Stackoverflow question: EF Core: How to cascade delete when DB-first FK is the wrong way around?

@ajcvickers
Copy link
Member

@bert2 This is probably best handled by logic in an overridden SaveChanges method. For example:

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);
}

Some notes about this code:

  • It requires that the CritiqueText entities are loaded
  • The Review instances are themselves getting deleted through the cascade delete mechanism, and this mechanism doesn't kick-in until SaveChanges runs. This means the Review entities are not yet marked as Deleted when SaveChanges starts. This is controllable in 3.0--see Allow cascade delete timing to be configured #14470. For now I have included a little bit of internal code that will trigger the cascade mechanism.
  • Auto DetectChanges is disabled for rest of the processing, since we know we're working with fresh information from the first call.

@ajcvickers ajcvickers added closed-no-further-action The issue is closed and no further action is planned. customer-reported labels Feb 11, 2019
bert2 added a commit to bert2/ef-vs-garbage-db that referenced this issue Feb 15, 2019
bert2 added a commit to bert2/ef-vs-garbage-db that referenced this issue Feb 15, 2019
@bert2
Copy link
Author

bert2 commented Feb 15, 2019

Nice, this worked perfectly. Thanks for the quick answer!

I refactored the solution to be based on attributes. Interested parties can find it in the test project repo.

@voroninp
Copy link

Unfortunately, it looks more like a hack rather than a robust solution. Moreover, if you have plans to allow configuring shapes of aggregates, you'll probably need to implement deletion of an aggregate as well irrespective of db's dependency direction.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

3 participants