dotnet / dotnet/efcore

Enable IIdentityResolutionInterceptor for queries

Open
#37,574 4 comments 0 reactions 1 assignee Assigned to @AndriySvyryd View on GitHub
area-interception area-query customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

### Bug description

I think there is an issue with IIdentityResolutionInterceptor, where it will not intercept the identity resolution between entities that are returned multiple times from different queries.

The basic pattern is:

```
var blog = context.Blogs.First();
context.Database.ExecuteSqlRaw("UPDATE Blogs SET Title = 'Changed' WHERE Id = 1");
var sameBlog = context.Blogs.First();
```

which will not be intercepted. Instead the old version of the entity will just "win".

I created a minimal demo showing how it will intercept the resolution in entities that are manually attached, but not ones that are returned from queries: https://github.com/jeremysalwen/IIdentityResolutionInterceptorDemonstration

### Your code

```csharp
https://github.com/jeremysalwen/IIdentityResolutionInterceptorDemonstration

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;

using var context = new BlogContext();
context.Database.EnsureCreated();
context.Blogs.Add(new Blog { Title = "Original" });
context.SaveChanges();

Console.WriteLine("=== Scenario A: Re-query ===");
var blog = context.Blogs.First();
context.Database.ExecuteSqlRaw("UPDATE Blogs SET Title = 'Changed' WHERE Id = 1");
var sameBlog = context.Blogs.First();
Console.WriteLine($"Title: {sameBlog.Title}");

Console.WriteLine();
Console.WriteLine("=== Scenario B: Attach ===");
context.Attach(new Blog { Id = 1, Title = "Attached" });

public class Blog
{
public int Id { get; set; }
public string Title { get; set; } = "";
public override string ToString() => $"Blog {{ Id = {Id}, Title = {Title} }}";
}

public class BlogContext : DbContext
{
public DbSet Blogs => Set();

protected override void OnConfiguring(DbContextOptionsBuilder options) => options
.UseSqlite("Data Source=demo.db")
.AddInterceptors(new LoggingInterceptor());
}

public class LoggingInterceptor : IIdentityResolutionInterceptor
{
public void UpdateTrackedInstance(IdentityResolutionInterceptionData data, EntityEntry existing, object newInstance)
=> Console.WriteLine($"Interceptor called: existing={existing.Entity}, new={newInstance}");
}
```

### Stack traces

```text

```

### Verbose output

```text

```

### EF Core version

9.0.205

### Database provider

SQLite

### Target framework

.NET 9.0

### Operating system

Windows 11

### IDE

_No response_

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.