Compiled query cache issues with ConstantExpression
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
Lets assume we want to execute 2 queries that contains a constant expression with the same object but with different values inside this object. In provided example query has been created with a help of **EF.Const** and with a object (value) of type **List**.
For **queryResultWithOneId** query has been generated correctly.
Generated query:
`SELECT [i].[Id]
FROM [Items] AS [i]
WHERE [i].[Id] = 1`
However after changing contents of **idList** by adding a value (2) and executing the same query second time, generated query is no longer correct as it is exactly the same as the one generated for **queryResultWithOneId**.
Generated query:
`SELECT [i].[Id]
FROM [Items] AS [i]
WHERE [i].[Id] = 1`
Correct query:
`SELECT [i].[Id]
FROM [Items] AS [i]
WHERE [i].[Id] IN (1, 2)`
### How to reproduce
```C#
using Microsoft.EntityFrameworkCore;
using var context = new TestDbContext();
var idList = new List { 1 };
var queryResultWithOneId = context.Items
.Where(x => EF.Constant(idList).Contains(x.Id))
.ToQueryString();
idList.Add(2);
var queryResultWithTwoId = context.Items
.Where(x => EF.Constant(idList).Contains(x.Id))
.ToQueryString();
Console.ReadLine();
public class TestDbContext : DbContext
{
public DbSet Items { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
}
}
public class Item
{
public int Id { get; set; }
}
```
### Investigation
After a brief investigation, it became apparent that **ICompiledQueryCache** got a hit and returned a complied query from cache. It seems that **ICompiledQueryCacheKeyGenerator** generates a key based of created expression. This expression contains **ConstantExpression** that holds a reference (or value, depending on the provided constant type) to provided value. Consequently, modifying contents of **idList** also changes value stored in expression stored within cache key. When executing a second query, the query plan cache key has already been altered due to changing **idList** contents. Subsequently, **_memoryCache.TryGetValue** in **ICompiledQueryCache** got a hit and returns compiled query that was created when **idList** contained old values.
In sumary, changing values in reference types referenced in `ConstantExpression` changes cache key of previously generated query plan which causes cache to return invalid query plans.
### Include provider and version information
EF Core version:
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 8.0
Operating system: Windows 10
IDE: Visual Studio 2022 17.8.6
Contributor guide
Assessment
This issue has not been assessed yet.