Changes made on a foreign key id is lost if we read the foreign object after assignation in some cases
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
## Issue with foreign key ids changes that is lost after read
We have a weird issue with the change tracker since upgrade from .net 6 to .net 7. This issue is also present on .net 8.0.1.
I tracked down the issue and was able to write a really minimal example to reproduce it.
- Two parent entities must be read including the same child entity.
- Change the foreign key id of one of the parent to point to another child object. The new child object is not currently tracked.
- Then, if, somewhere before the SaveChanges (or any DetectChanges), we read the new object (not currently tracked) then the foreign key change is lost.
### Minimal code to reproduce the issue
```C#
public class Parent1
{
public int Id { get; set; }
public ReferenceTable? ReferenceTable { get; set; }
public int? ReferenceTableId { get; set; }
}
public class Parent2
{
public int Id { get; set; }
public ReferenceTable? ReferenceTable { get; set; }
public int? ReferenceTableId { get; set; }
}
public class ReferenceTable
{
public int Id { get; set; }
public string Value { get; set; } = string.Empty;
}
```
```C#
using Microsoft.EntityFrameworkCore;
int newId;
// Data seed
using (var context = new TestContext())
{
var referenceTable1 = new ReferenceTable { Value = "Record 1" };
var referenceTable2 = new ReferenceTable { Value = "Record 2" };
context.Add(referenceTable1);
context.Add(referenceTable2);
context.SaveChanges();
context.Add(new Parent1 { ReferenceTableId = referenceTable1.Id });
context.Add(new Parent2 { ReferenceTableId = referenceTable1.Id });
context.SaveChanges();
newId = referenceTable2.Id;
}
// Minimal code for bug reproduction
using (var context = new TestContext())
{
// Two parents that include the same referenceTable item are read
var parent1 = await context.Parents1
.Include(x => x.ReferenceTable)
.SingleAsync();
var parent2 = await context.Parents2
.Include(x => x.ReferenceTable)
.SingleAsync();
// Change the foreign key of reference table from 1 to 2 (2 is not tracked)
Console.WriteLine($"Change the foreign key of reference table from {parent2.ReferenceTableId} to {newId} (new one is not currently tracked)");
parent2.ReferenceTableId = newId;
Console.WriteLine($"The value is now : {parent2.ReferenceTableId}");
// Read the new one (this cause it to be tracked)
var referenceTableSecondRecord = await context.ReferenceTables
.SingleOrDefaultAsync(x => x.Id == newId);
// In .net 6, the value here is the new one. However in .net 7 the new value of the foreign key is lost.
Console.WriteLine($"After the read operation, the value is : {parent2.ReferenceTableId}");
}
```
#### Output:
```
Change the foreign key of reference table from 5 to 6 (new one is not currently tracked)
The value is now : 6
After the read operation, the value is : 5
```
Full project here: [ConsoleApp.zip](https://github.com/dotnet/efcore/files/14211375/ConsoleApp.zip)
### Provider and version information
EF Core version: 7.0.15 (also tested 8.0.1)
Database provider: tested PostgreSql and SQlite. The issue arise with both.
Target framework: 7.0.15 (also tested 8.0.1)
Operating system: Windows and also tested with docker (mcr.microsoft.com/dotnet/aspnet:7.0-bullseye-slim)
IDE: Visual Studio 2022 17.8
Contributor guide
Assessment
This issue has not been assessed yet.