@@ -310,34 +310,42 @@ protected override Expression VisitParameter(ParameterExpression p)
310
310
311
311
protected override Expression VisitMemberAccess ( MemberExpression ma )
312
312
{
313
- if ( ma . Expression != null )
314
- if ( ma . Expression . Type != ma . Member . ReflectedType
315
- && ma . Member is PropertyInfo
316
- && ! ma . Member . ReflectedType . IsInterface )
313
+ var memberInfo = ma . Member ;
314
+ var sourceExpression = ma . Expression ;
315
+
316
+ if ( sourceExpression != null ) {
317
+ if ( sourceExpression . Type != memberInfo . ReflectedType
318
+ && memberInfo is PropertyInfo
319
+ && ! memberInfo . ReflectedType . IsInterface ) {
317
320
ma = Expression . MakeMemberAccess (
318
- ma . Expression , ma . Expression . Type . GetProperty ( ma . Member . Name , ma . Member . GetBindingFlags ( ) ) ) ;
319
- var customCompiler = context . CustomCompilerProvider . GetCompiler ( ma . Member ) ;
321
+ sourceExpression , sourceExpression . Type . GetProperty ( memberInfo . Name , memberInfo . GetBindingFlags ( ) ) ) ;
322
+
323
+ memberInfo = ma . Member ;
324
+ sourceExpression = ma . Expression ;
325
+ }
326
+ }
327
+
328
+ var customCompiler = context . CustomCompilerProvider . GetCompiler ( memberInfo ) ;
320
329
321
330
// Reflected type doesn't have custom compiler defined, so falling back to base class compiler
322
- var declaringType = ma . Member . DeclaringType ;
323
- Type reflectedType = ma . Member . ReflectedType ;
331
+ var declaringType = memberInfo . DeclaringType ;
332
+ var reflectedType = memberInfo . ReflectedType ;
324
333
if ( customCompiler == null && declaringType != reflectedType && declaringType . IsAssignableFrom ( reflectedType ) ) {
325
334
var root = declaringType ;
326
335
var current = reflectedType ;
327
336
while ( current != root && customCompiler == null ) {
328
337
current = current . BaseType ;
329
- var member = current . GetProperty ( ma . Member . Name , BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) ;
338
+ var member = current . GetProperty ( memberInfo . Name , BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) ;
330
339
customCompiler = context . CustomCompilerProvider . GetCompiler ( member ) ;
331
340
}
332
341
}
333
342
334
- if ( customCompiler != null ) {
335
- var member = ma . Member ;
336
- var expression = customCompiler . Invoke ( ma . Expression , ArrayUtils < Expression > . EmptyArray ) ;
343
+ if ( customCompiler != null ) {
344
+ var expression = customCompiler . Invoke ( sourceExpression , ArrayUtils < Expression > . EmptyArray ) ;
337
345
if ( expression == null ) {
338
- if ( member . ReflectedType . IsInterface )
346
+ if ( reflectedType . IsInterface )
339
347
return Visit ( BuildInterfaceExpression ( ma ) ) ;
340
- if ( member . ReflectedType . IsClass )
348
+ if ( reflectedType . IsClass )
341
349
return Visit ( BuildHierarchyExpression ( ma ) ) ;
342
350
}
343
351
else
@@ -346,47 +354,49 @@ protected override Expression VisitMemberAccess(MemberExpression ma)
346
354
347
355
if ( context . Evaluator . CanBeEvaluated ( ma ) && context . ParameterExtractor . IsParameter ( ma ) ) {
348
356
if ( typeof ( IQueryable ) . IsAssignableFrom ( ma . Type ) ) {
349
- Func < IQueryable > lambda = FastExpression . Lambda < Func < IQueryable > > ( ma ) . CachingCompile ( ) ;
350
- IQueryable rootPoint = lambda ( ) ;
357
+ var lambda = FastExpression . Lambda < Func < IQueryable > > ( ma ) . CachingCompile ( ) ;
358
+ var rootPoint = lambda ( ) ;
351
359
if ( rootPoint != null )
352
360
return base . Visit ( rootPoint . Expression ) ;
353
361
}
354
362
return ma ;
355
363
}
356
- if ( ma . Expression == null ) {
357
- if ( typeof ( IQueryable ) . IsAssignableFrom ( ma . Type ) ) {
364
+ if ( sourceExpression == null ) {
365
+ if ( typeof ( IQueryable ) . IsAssignableFrom ( ma . Type ) ) {
358
366
var lambda = FastExpression . Lambda < Func < IQueryable > > ( ma ) . CachingCompile ( ) ;
359
367
var rootPoint = lambda ( ) ;
360
- if ( rootPoint != null )
368
+ if ( rootPoint != null )
361
369
return VisitSequence ( rootPoint . Expression ) ;
362
370
}
363
371
}
364
- else if ( ma . Expression . NodeType == ExpressionType . Constant ) {
365
- var rfi = ma . Member as FieldInfo ;
366
- if ( rfi != null && ( rfi . FieldType . IsGenericType && typeof ( IQueryable ) . IsAssignableFrom ( rfi . FieldType ) ) ) {
372
+ else if ( sourceExpression . NodeType == ExpressionType . Constant ) {
373
+ if ( memberInfo is FieldInfo rfi && ( rfi . FieldType . IsGenericType && typeof ( IQueryable ) . IsAssignableFrom ( rfi . FieldType ) ) ) {
367
374
var lambda = FastExpression . Lambda < Func < IQueryable > > ( ma ) . CachingCompile ( ) ;
368
375
var rootPoint = lambda ( ) ;
369
- if ( rootPoint != null )
376
+ if ( rootPoint != null )
370
377
return VisitSequence ( rootPoint . Expression ) ;
371
378
}
372
379
}
373
- else if ( ma . Expression . GetMemberType ( ) == MemberType . Entity && ma . Member . Name != "Key" ) {
374
- var type = ma . Expression . Type ;
375
- var parameter = ma . Expression as ParameterExpression ;
376
- if ( parameter != null ) {
380
+ else if ( sourceExpression . GetMemberType ( ) == MemberType . Entity && memberInfo . Name != "Key" ) {
381
+ var type = sourceExpression . Type ;
382
+ if ( sourceExpression is ParameterExpression parameter ) {
377
383
var projection = context . Bindings [ parameter ] ;
378
384
type = projection . ItemProjector . Item . Type ;
379
385
}
380
- if ( ! context . Model . Types [ type ] . Fields . Contains ( context . Domain . Handlers . NameBuilder . BuildFieldName ( ( PropertyInfo ) ma . Member ) ) )
381
- throw new NotSupportedException ( String . Format ( Strings . ExFieldMustBePersistent , ma . ToString ( true ) ) ) ;
386
+ if ( ! context . Model . Types [ type ] . Fields . Contains ( context . Domain . Handlers . NameBuilder . BuildFieldName ( ( PropertyInfo ) memberInfo ) ) ) {
387
+ throw new NotSupportedException ( string . Format ( Strings . ExFieldMustBePersistent , ma . ToString ( true ) ) ) ;
388
+ }
382
389
}
383
390
Expression source ;
384
391
using ( state . CreateScope ( ) ) {
385
392
// state.BuildingProjection = false;
386
- source = Visit ( ma . Expression ) ;
393
+ source = Visit ( sourceExpression ) ;
387
394
}
388
395
389
- var result = GetMember ( source , ma . Member , ma ) ;
396
+ var result = context . CheckIfQueryReusePossible ( memberInfo )
397
+ ? GetMemberWithRemap ( source , memberInfo , ma )
398
+ : GetMember ( source , memberInfo , ma ) ;
399
+
390
400
return result ?? base . VisitMemberAccess ( ma ) ;
391
401
}
392
402
@@ -1407,6 +1417,59 @@ bool propertyFilter(PersistentFieldExpression f)
1407
1417
: result ;
1408
1418
}
1409
1419
1420
+ private Expression GetMemberWithRemap ( Expression expression , MemberInfo memberInfo , Expression sourceExpression )
1421
+ {
1422
+ var original = GetMember ( expression , memberInfo , sourceExpression ) ;
1423
+ if ( original . IsSubqueryExpression ( ) ) {
1424
+ var subquery = ( SubQueryExpression ) original ;
1425
+ var projectionExpression = subquery . ProjectionExpression ;
1426
+ var itemProjector = projectionExpression . ItemProjector ;
1427
+ var columnIndexes = itemProjector . GetColumns ( ColumnExtractionModes . KeepSegment ) . ToArray ( ) ;
1428
+
1429
+ var expReplacer = new ExtendedExpressionReplacer ( ( e ) => {
1430
+ if ( e is GroupingExpression ge && ge . SelectManyInfo . GroupByProjection != null ) {
1431
+ var geProjectionExpression = ge . ProjectionExpression ;
1432
+ var geItemProjector = geProjectionExpression . ItemProjector ;
1433
+ var columnIndexes = geItemProjector . GetColumns ( ColumnExtractionModes . KeepSegment ) . ToArray ( ) ;
1434
+
1435
+ var newProjectionExpression = new ProjectionExpression ( geProjectionExpression . Type ,
1436
+ geItemProjector . Remap ( geItemProjector . DataSource , columnIndexes ) ,
1437
+ geProjectionExpression . TupleParameterBindings ) ;
1438
+
1439
+ var groupByProjection = ge . SelectManyInfo . GroupByProjection ;
1440
+ var groupByProjector = groupByProjection . ItemProjector ;
1441
+ var groupByColumnIndexes = groupByProjector . GetColumns ( ColumnExtractionModes . KeepSegment ) . ToArray ( ) ;
1442
+
1443
+ var newGroupByProjection = new ProjectionExpression ( groupByProjection . Type ,
1444
+ groupByProjector . Remap ( groupByProjector . DataSource , groupByColumnIndexes ) ,
1445
+ groupByProjection . TupleParameterBindings ) ;
1446
+
1447
+ var result = new GroupingExpression ( ge . Type ,
1448
+ ge . OuterParameter , ge . DefaultIfEmpty , newProjectionExpression , ge . ApplyParameter , ge . KeyExpression ,
1449
+ new GroupingExpression . SelectManyGroupingInfo ( newGroupByProjection ) ) ;
1450
+ return result ;
1451
+ }
1452
+ else
1453
+ return null ;
1454
+ } ) ;
1455
+
1456
+ var newItem = expReplacer . Replace ( itemProjector . Item ) ;
1457
+ var staysTheSame = newItem == itemProjector . Item ;
1458
+ var newItemProjector = staysTheSame
1459
+ ? itemProjector . Remap ( itemProjector . DataSource , columnIndexes )
1460
+ : new ItemProjectorExpression ( newItem , itemProjector . DataSource , itemProjector . Context ) ;
1461
+
1462
+ var result = new SubQueryExpression ( subquery . Type ,
1463
+ subquery . OuterParameter ,
1464
+ subquery . DefaultIfEmpty ,
1465
+ new ProjectionExpression ( projectionExpression . Type , newItemProjector , projectionExpression . TupleParameterBindings ) ,
1466
+ subquery . ApplyParameter ,
1467
+ subquery . ExtendedType ) ;
1468
+ return result ;
1469
+ }
1470
+ return original ;
1471
+ }
1472
+
1410
1473
private Expression GetConditionalMember ( Expression expression , MemberInfo member , Expression sourceExpression )
1411
1474
{
1412
1475
var ce = expression as ConditionalExpression ;
0 commit comments