Enable IIdentityResolutionInterceptor for queries
- 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
Assessment
This issue has not been assessed yet.