@@ -310,34 +310,42 @@ protected override Expression VisitParameter(ParameterExpression p)
310310
311311 protected override Expression VisitMemberAccess ( MemberExpression ma )
312312 {
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 ) {
317320 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 ) ;
320329
321330 // 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 ;
324333 if ( customCompiler == null && declaringType != reflectedType && declaringType . IsAssignableFrom ( reflectedType ) ) {
325334 var root = declaringType ;
326335 var current = reflectedType ;
327336 while ( current != root && customCompiler == null ) {
328337 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 ) ;
330339 customCompiler = context . CustomCompilerProvider . GetCompiler ( member ) ;
331340 }
332341 }
333342
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 ) ;
337345 if ( expression == null ) {
338- if ( member . ReflectedType . IsInterface )
346+ if ( reflectedType . IsInterface )
339347 return Visit ( BuildInterfaceExpression ( ma ) ) ;
340- if ( member . ReflectedType . IsClass )
348+ if ( reflectedType . IsClass )
341349 return Visit ( BuildHierarchyExpression ( ma ) ) ;
342350 }
343351 else
@@ -346,47 +354,49 @@ protected override Expression VisitMemberAccess(MemberExpression ma)
346354
347355 if ( context . Evaluator . CanBeEvaluated ( ma ) && context . ParameterExtractor . IsParameter ( ma ) ) {
348356 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 ( ) ;
351359 if ( rootPoint != null )
352360 return base . Visit ( rootPoint . Expression ) ;
353361 }
354362 return ma ;
355363 }
356- if ( ma . Expression == null ) {
357- if ( typeof ( IQueryable ) . IsAssignableFrom ( ma . Type ) ) {
364+ if ( sourceExpression == null ) {
365+ if ( typeof ( IQueryable ) . IsAssignableFrom ( ma . Type ) ) {
358366 var lambda = FastExpression . Lambda < Func < IQueryable > > ( ma ) . CachingCompile ( ) ;
359367 var rootPoint = lambda ( ) ;
360- if ( rootPoint != null )
368+ if ( rootPoint != null )
361369 return VisitSequence ( rootPoint . Expression ) ;
362370 }
363371 }
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 ) ) ) {
367374 var lambda = FastExpression . Lambda < Func < IQueryable > > ( ma ) . CachingCompile ( ) ;
368375 var rootPoint = lambda ( ) ;
369- if ( rootPoint != null )
376+ if ( rootPoint != null )
370377 return VisitSequence ( rootPoint . Expression ) ;
371378 }
372379 }
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 ) {
377383 var projection = context . Bindings [ parameter ] ;
378384 type = projection . ItemProjector . Item . Type ;
379385 }
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+ }
382389 }
383390 Expression source ;
384391 using ( state . CreateScope ( ) ) {
385392// state.BuildingProjection = false;
386- source = Visit ( ma . Expression ) ;
393+ source = Visit ( sourceExpression ) ;
387394 }
388395
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+
390400 return result ?? base . VisitMemberAccess ( ma ) ;
391401 }
392402
@@ -1407,6 +1417,59 @@ bool propertyFilter(PersistentFieldExpression f)
14071417 : result ;
14081418 }
14091419
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+
14101473 private Expression GetConditionalMember ( Expression expression , MemberInfo member , Expression sourceExpression )
14111474 {
14121475 var ce = expression as ConditionalExpression ;
0 commit comments