protected override Expression VisitMemberAccess(MemberExpression memberExpression) { MemberExpression expression; var visited = new List<MemberExpression>(); var root = memberExpression.Expression.StripForIncludeScanning(); var memberIsDataAccessObjectGatheringForProjection = false; // Don't perform implicit joins for RelatedDataAccessObject collections as those currently turn into N+1 queries if (this.nesting < 1 && (memberExpression.Type.GetSequenceElementType()?.IsDataAccessObjectType() ?? false)) { return base.VisitMemberAccess(memberExpression); } if (memberExpression.Type.IsTypeRequiringJoin()) { if (this.forProjection) { memberIsDataAccessObjectGatheringForProjection = true; expression = memberExpression.StripForIncludeScanning() as MemberExpression; } else { expression = memberExpression.Expression.StripForIncludeScanning() as MemberExpression; if (expression == null || !expression.Expression.StripForIncludeScanning().Type.IsTypeRequiringJoin()) { return memberExpression; } } } else { if (memberExpression.Expression?.StripForIncludeScanning() == null) { return memberExpression; } var typeDescriptor = this.model.TypeDescriptorProvider.GetTypeDescriptor(memberExpression.Expression.StripForIncludeScanning().Type); if (typeDescriptor == null) { return Expression.MakeMemberAccess(this.Visit(memberExpression.Expression.StripForIncludeScanning()), memberExpression.Member); } var property = typeDescriptor.GetPropertyDescriptorByPropertyName(memberExpression.Member.Name); if (property.IsPrimaryKey && memberExpression.Expression.StripForIncludeScanning() is MemberExpression) { expression = ((MemberExpression)memberExpression.Expression.StripForIncludeScanning()).Expression.StripForIncludeScanning() as MemberExpression; } else { expression = memberExpression.Expression.StripForIncludeScanning() as MemberExpression; } } var currentExpression = expression.StripForIncludeScanning() as MemberExpression; while (currentExpression?.Member is PropertyInfo) { visited.Add(currentExpression); root = currentExpression.Expression.StripForIncludeScanning(); currentExpression = root as MemberExpression; if (currentExpression == null) { root = this.GetExpression(root).StripForIncludeScanning(); currentExpression = root as MemberExpression; } } var includedPathSkip = 0; var i = 0; foreach (var current in visited) { if (!current.Member.ReflectedType.IsTypeRequiringJoin() || current == this.currentParent /* @see: Test_Select_Project_Related_Object_And_Include1 */) { includedPathSkip = visited.Count - i; break; } i++; } visited.Reverse(); i = 0; currentExpression = expression; while (currentExpression?.Member is PropertyInfo) { var path = new PropertyPath(c => c.Name, visited.Select(c=> (PropertyInfo)c.Member).Take(visited.Count - i).ToArray()); ReferencedRelatedObject objectInfo; if (path.Length == 0) { break; } if (!path.Last.ReflectedType.IsTypeRequiringJoin()) { this.rootExpressionsByPath[path] = currentExpression; break; } if (!this.results.TryGetValue(path, out objectInfo)) { var includedPropertyPath = new PropertyPath(c => c.Name, path.Skip(includedPathSkip)); objectInfo = new ReferencedRelatedObject(path, includedPropertyPath, sourceParameterExpression); this.results[path] = objectInfo; } this.referencedRelatedObjects.Add(objectInfo); if (memberIsDataAccessObjectGatheringForProjection) { objectInfo.TargetExpressions.Add(currentExpression); } else if (currentExpression == expression) { objectInfo.TargetExpressions.Add(expression); } i++; currentExpression = currentExpression.Expression.StripForIncludeScanning() as MemberExpression; } return memberExpression; }