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

Entity cannot be tracked because another instance with the same key value is already being tracked for optional relationships are not automatically deleted #33610

Closed
archer87pl opened this issue Apr 24, 2024 · 7 comments

Comments

@archer87pl
Copy link

archer87pl commented Apr 24, 2024

Background:
We are doing upgrade from .NET 6.0 to .NET 8.0 and spent several hours identyfing why our code fails in new EF 8.
It appears that there was breaking change in EF Core 7.0 Orphaned dependents of optional relationships are not automatically deleted https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/breaking-changes?tabs=v7#optional-deletes

Right now dependenant enties are not being deleted which is causing the error
System.InvalidOperationException: The instance of entity type 'CompanyPropertyEntity' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
You suggest to use context.SavingChanges event but it is not possible as tracker is in already broken state so the workaround is completly useless.

This is happening when you try to set the new values for simple model (Please note that CompanyId is nullable in CompanyProperty entity):

var company = await GetCompanyById(DefaultCompanyId);

var companyToUpdate = new CompanyEntity()
{
Id = DefaultCompanyId,
Name = "Updated name",
CompanyProperties = new List()
{
new CompanyPropertyEntity()
{
Id = 1,
Name = "Prop 1"
},
new CompanyPropertyEntity()
{
Id = 2,
Name = "Prop 2"
},
}
};

company.CompanyProperties = companyToUpdate.CompanyProperties;

await _db.SaveChangesAsync(); ```

This is riduculus, how should we update the entities in EF 7/8 for such a simple case and avoid throwing that exception?
Is it possible to disable tracking for orpahned entities in EF 7/8?

Example project: 

[https://github.com/archer87pl/ef8orphanedentity/tree/main/OrphanedEntities](url)

EF Core version: 8.0.4
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 8.0
Operating system: Windows 11
IDE: Visual Studio 2022 17.9.6
@ajcvickers
Copy link
Contributor

@archer87pl The repro code in your example project throws the same exception on 6.0.29 as it does on 8.0.4.

@archer87pl
Copy link
Author

@ajcvickers I have updated the repro code.
In earlier revision, the DeleteBehavior for the Entity was set to NoAction, and this caused the code to fail in versions 6.0.29 and 8.0.4. However, when the DeleteBehavior is set to Cascade, the failure only occurs in version 8.0.4. What's the best way to implement a straightforward update in Entity Framework 8?"

@ajcvickers
Copy link
Contributor

@archer87pl This appears to be directly the case you referenced above: Orphaned dependents of optional relationships are not automatically deleted. The mitigations there are still the best way to deal with this. Issue #10066 tracks being able to configure an optional relationship to delete orphans without making it required or manually processing child entities.

@archer87pl
Copy link
Author

@ajcvickers so which solution do you recommend to migrate to EF8 from EF6 for such a configuration? Should we manually process entities (really!?) as we don't want required relationships?

@ajcvickers
Copy link
Contributor

@archer87pl Yes, if you want to delete orphans on optional relationships, then this needs manual processing. Otherwise the relationship will be severed but the entity will not be deleted automatically, since it can exist without a parent, which is the normal behavior for optional relationships. The only reason required relationships are different is that the child entity must have a parent or else be deleted from the database.

@archer87pl
Copy link
Author

archer87pl commented May 10, 2024

@ajcvickers It's a bit odd that what should be a simple update now requires so much custom code. Are you planning to implement a feature flag to revert to the previous behavior?

In the current state, every time I call the change tracker, I get the same exception:

_db.ChangeTracker.Entries<CompanyEntity>().ToList();

Regarding your proposed solution:

context.SavingChanges += (c, _) => { foreach (var entry in ((DbContext)c!).ChangeTracker .Entries<Blog>() .Where(e => e.State == EntityState.Modified)) { if (entry.Reference(e => e.Author).CurrentValue == null) { entry.State = EntityState.Deleted; } } };
This approach also doesn't work because accessing the ChangeTracker after modifying state leads to the same exception.

@ajcvickers
Copy link
Contributor

Are you planning to implement a feature flag to revert to the previous behavior?

This is tracked by #10066.

@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale May 15, 2024
@ajcvickers ajcvickers removed their assignment May 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants