Precompiled queries: subexpressions extracted from lambda exprs contain lambda-bound variables
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
### Bug description
Sometimes the code generated for precompiled queries declares subexpressions that are later used in subsequent expressions, probably to avoid duplication. The problem is that subexpressions can be part of the lambda and contain variables declared only in the scope of the lambda:
```
var unknown = left == null;
var unknown0 = right == null;
var unknown1 = right != null;
var unknown2 = bool (object left, object right) => (unknown ? unknown0 : unknown1 && ((int)left) == ((int)right));
var parentIdentifierValueComparers = new Func[]
{
unknown2
};
var selfIdentifierValueComparers = new Func[]
{
unknown2,
bool (object left, object right) => (unknown ? unknown0 : unknown1 && ((string)left) == ((string)right))
};
```
Here `left` and `right` in the first three `unknown` vars get outside of the lambda and lead to errors when compiling:
```
/workspaces/ef-play/commexpr/Generated/Program.EFInterceptors.Context.cs(76,27): error CS0103: The name 'left' does not exist in the current context
/workspaces/ef-play/commexpr/Generated/Program.EFInterceptors.Context.cs(77,28): error CS0103: The name 'right' does not exist in the current context
/workspaces/ef-play/commexpr/Generated/Program.EFInterceptors.Context.cs(78,28): error CS0103: The name 'right' does not exist in the current context
```
So far there was only one case where this problem manifested, when compiling a query which includes related entities and that entities have a compound primary key. The workaround for this case is to add a single-column primary key (which leads to schema change), or to avoid including related entities (which may lead to extra round-trip to database). I'd be nice to know if there is a better workaround.
See attached project with minimal example.
Can be reproduced with:
```
> dotnet publish --ucr --sc
```
[commexpr.zip](https://github.com/user-attachments/files/26139527/commexpr.zip)
### Your code
```csharp
--- Model.cs
public class Blog
{
public int Id { get; set; }
public ICollection Posts { get; set; } = new List();
}
public class Post
{
// public int Id { get; set; }
public int BlogId { get; set; }
public required string Slug { get; set; }
}
--- Context.cs
using Microsoft.EntityFrameworkCore;
public class Context : DbContext
{
public DbSet Blogs { get; set; } = null!;
public DbSet Posts { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlite("Data Source=tmp.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity()
.HasOne()
.WithMany(b => b.Posts)
.IsRequired();
modelBuilder.Entity()
.HasKey(p => new { p.BlogId, p.Slug });
}
}
--- Program.cs
using Microsoft.EntityFrameworkCore;
using var context = new Context();
_ = context.Blogs
.Include(b => b.Posts)
.ToList();
```
### Stack traces
```text
```
### Verbose output
```text
```
### EF Core version
10.0.3, 11.0.0-preview.2.26159.112
### Database provider
Microsoft.EntityFrameworkCore.Sqlite
### Target framework
.NET 10, .NET 11
### Operating system
Ubuntu 24.04.4 LTS
### IDE
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.