dotnet / dotnet/efcore

Precompiled queries: subexpressions extracted from lambda exprs contain lambda-bound variables

Open
#37,960 1 comment 0 reactions 0 assignees View on GitHub
area-precompiled-queries customer-reported
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

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.