Open BladeWise opened 2 days ago
Confirmed regression from 8.0 to 9.0 - another thing to come out of the funcletizer rewrite, most likely.
The issue isn't the Expression variable, it's the convert-to-Object node that's added inside; the same error happens with:
var v = 1;
var q = await context.Set<Blog>().OrderBy(x => (object)v).ToListAsync();
This means that it's possible to work around this simply by having the lambda return the exact type, so no up-cast is needed:
Expression<Func<Dummy, int>> f = x => v;
The problem is that when we process the body of a lambda in the funcletizer, we incorrectly leave the state as EvaluatableWithCapturedVariables, after we've already evaluated the body inside; this causes the whole lambda to be evaluated again (technically the wrapping Quote node), and the attempt to run the LINQ interpreter over the ParameterExpression inside causes the exception. The correct state after evaluating something is NotEvaluatable, so that we don't attempt to re-evaluate something that already has been evaluated.
Note that the issue no longer reproes after recent switch to QueryParameterExpression in 10.0 (#35101), but for the reasons aren't material to this issue: the evaluated node is now a QueryParameterExpression rather than a ParameterExpression, and the LINQ interpreter surprisingly doesn't error on that unknown node type. But the bug is still there and could probably manifest in other ways.
@roji I noticed that the issue was with conversion and applied the exact workaround you proposed. Thanks for sharing the actual root cause!
Using EFCore 9 the following code (which uses a closure in an order-by condition)
throws at runtime with the following error:
Stacktrace
```Shell System.InvalidOperationException: An exception was thrown while attempting to evaluate the LINQ query parameter expression 'x => Convert(__v_0, Object)'. See the inner exception for more information. ---> System.InvalidOperationException: unbound variable: __v_0 at System.Linq.Expressions.Interpreter.LightCompiler.EnsureAvailableForClosure(ParameterExpression expr) at System.Linq.Expressions.Interpreter.LightCompiler.CompileQuoteUnaryExpression(Expression expr) at System.Linq.Expressions.Interpreter.LightCompiler.Compile(Expression expr) at System.Linq.Expressions.Interpreter.LightCompiler.CompileConvertUnaryExpression(Expression expr) at System.Linq.Expressions.Interpreter.LightCompiler.Compile(Expression expr) at System.Linq.Expressions.Interpreter.LightCompiler.CompileTop(LambdaExpression node) at System.Linq.Expressions.Expression`1.Compile(Boolean preferInterpretation) at Microsoft.EntityFrameworkCore.Query.Internal.ExpressionTreeFuncletizer.The same code completes successfully using EFCore 8. Moreover, if the boxing is moved out of the expression like below
the queryable is interpreted successfully.
The database provider is not relevant, because the exception is thrown at interpretation time (nevertheless, I have tested with both InMemory and PostgreSQL).
I have created a gist to reproduce the issue
Gist output
```Shell dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401] An 'IServiceProvider' was created for internal use by Entity Framework. warn: Microsoft.EntityFrameworkCore.Model.Validation[10400] Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data; this mode should only be enabled during development. dbug: Microsoft.EntityFrameworkCore.Infrastructure[10403] Entity Framework Core 9.0.0 initialized 'SampleDbContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:9.0.0' with options: SensitiveDataLoggingEnabled StoreName=test dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10806] Context 'SampleDbContext' started tracking 'Dummy' entity with key '{Id: 1}'. dbug: Microsoft.EntityFrameworkCore.Update[10004] SaveChanges starting for 'SampleDbContext'. dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10800] DetectChanges starting for 'SampleDbContext'. dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10801] DetectChanges completed for 'SampleDbContext'. info: Microsoft.EntityFrameworkCore.Update[30100] Saved 1 entities to in-memory store. dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10807] The 'Dummy' entity with key '{Id: 1}' tracked by 'SampleDbContext' changed state from 'Added' to 'Unchanged'. dbug: Microsoft.EntityFrameworkCore.Update[10005] SaveChanges completed for 'SampleDbContext' with 1 entities written to the database. info: Program[0] Executing test OrderByClosureInt... fail: Program[0] Test OrderByClosureInt failed System.InvalidOperationException: An exception was thrown while attempting to evaluate the LINQ query parameter expression 'x => Convert(__v_0, Object)'. See the inner exception for more information. ---> System.InvalidOperationException: unbound variable: __v_0 at System.Linq.Expressions.Interpreter.LightCompiler.EnsureAvailableForClosure(ParameterExpression expr) at System.Linq.Expressions.Interpreter.LightCompiler.CompileQuoteUnaryExpression(Expression expr) at System.Linq.Expressions.Interpreter.LightCompiler.Compile(Expression expr) at System.Linq.Expressions.Interpreter.LightCompiler.CompileConvertUnaryExpression(Expression expr) at System.Linq.Expressions.Interpreter.LightCompiler.Compile(Expression expr) at System.Linq.Expressions.Interpreter.LightCompiler.CompileTop(LambdaExpression node) at System.Linq.Expressions.Expression`1.Compile(Boolean preferInterpretation) at Microsoft.EntityFrameworkCore.Query.Internal.ExpressionTreeFuncletizer.