dotnet / dotnet/efcore

Changes made on a foreign key id is lost if we read the foreign object after assignation in some cases

Open
#33,031 4 comments 2 reactions 0 assignees View on GitHub
area-change-tracking customer-reported needs-design Servicing-consider
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.