11using System . Collections ;
2- using System . Collections . Generic ;
32using System . Diagnostics . CodeAnalysis ;
4- using System . Linq ;
53using System . Linq . Expressions ;
64using System . Reflection ;
5+ using System . Runtime . CompilerServices ;
76using EntityFrameworkCore . Projectables . Extensions ;
87using Microsoft . EntityFrameworkCore . Metadata ;
98using Microsoft . EntityFrameworkCore . Query ;
@@ -12,9 +11,10 @@ namespace EntityFrameworkCore.Projectables.Services
1211{
1312 public sealed class ProjectableExpressionReplacer : ExpressionVisitor
1413 {
15- readonly IProjectionExpressionResolver _resolver ;
16- readonly ExpressionArgumentReplacer _expressionArgumentReplacer = new ( ) ;
17- readonly Dictionary < MemberInfo , LambdaExpression ? > _projectableMemberCache = new ( ) ;
14+ private readonly IProjectionExpressionResolver _resolver ;
15+ private readonly ExpressionArgumentReplacer _expressionArgumentReplacer = new ( ) ;
16+ private readonly Dictionary < MemberInfo , LambdaExpression ? > _projectableMemberCache = new ( ) ;
17+ private IQueryProvider ? _currentQueryProvider ;
1818 private bool _disableRootRewrite ;
1919 private IEntityType ? _entityType ;
2020
@@ -60,6 +60,9 @@ bool TryGetReflectedExpression(MemberInfo memberInfo, [NotNullWhen(true)] out La
6060 public Expression ? Replace ( Expression ? node )
6161 {
6262 _disableRootRewrite = false ;
63+ _currentQueryProvider = null ;
64+ _entityType = null ;
65+
6366 var ret = Visit ( node ) ;
6467
6568 if ( _disableRootRewrite )
@@ -190,6 +193,29 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
190193
191194 protected override Expression VisitMember ( MemberExpression node )
192195 {
196+ // Evaluate captured variables in closures that contain EF queries to inline them into the main query
197+ if ( node . Expression is ConstantExpression constant &&
198+ constant . Type . Attributes . HasFlag ( TypeAttributes . NestedPrivate ) &&
199+ Attribute . IsDefined ( constant . Type , typeof ( CompilerGeneratedAttribute ) , inherit : true ) )
200+ {
201+ try
202+ {
203+ var value = Expression
204+ . Lambda < Func < object > > ( Expression . Convert ( node , typeof ( object ) ) )
205+ . Compile ( )
206+ . Invoke ( ) ;
207+
208+ if ( value is IQueryable queryable && ReferenceEquals ( queryable . Provider , _currentQueryProvider ) )
209+ {
210+ return Visit ( queryable . Expression ) ;
211+ }
212+ }
213+ catch
214+ {
215+ // Ignore evaluation exceptions - continue with normal processing
216+ }
217+ }
218+
193219 var nodeExpression = node . Expression switch {
194220 UnaryExpression { NodeType : ExpressionType . Convert , Type : { IsInterface : true } type , Operand : { } operand }
195221 when type . IsAssignableFrom ( operand . Type )
@@ -232,6 +258,7 @@ protected override Expression VisitExtension(Expression node)
232258 if ( node is EntityQueryRootExpression root )
233259 {
234260 _entityType = root . EntityType ;
261+ _currentQueryProvider = root . QueryProvider ;
235262 }
236263
237264 return base . VisitExtension ( node ) ;
0 commit comments