private (Expression Operand, NavigationExpansionExpressionState State) ApplyIncludes(
            NavigationExpansionExpression navigationExpansionExpression)
        {
            var includeVisitor = new PendingSelectorIncludeVisitor();
            var rewrittenBody  = includeVisitor.Visit(navigationExpansionExpression.State.PendingSelector.Body);

            if (navigationExpansionExpression.State.PendingSelector.Body != rewrittenBody)
            {
                navigationExpansionExpression.State.PendingSelector      = Expression.Lambda(rewrittenBody, navigationExpansionExpression.State.PendingSelector.Parameters[0]);
                navigationExpansionExpression.State.ApplyPendingSelector = true;
            }

            if (includeVisitor.PendingIncludes.Count > 0)
            {
                var result = (Source : navigationExpansionExpression.Operand, Parameter : navigationExpansionExpression.State.CurrentParameter);
                foreach (var pendingIncludeNode in includeVisitor.PendingIncludes)
                {
                    result = NavigationExpansionHelpers.AddNavigationJoin(
                        result.Source,
                        result.Parameter,
                        pendingIncludeNode.SourceMapping,
                        pendingIncludeNode.NavTreeNode,
                        navigationExpansionExpression.State,
                        new List <INavigation>(),
                        include: true);
                }

                var pendingSelector = navigationExpansionExpression.State.PendingSelector;
                if (navigationExpansionExpression.State.CurrentParameter != result.Parameter)
                {
                    var pendingSelectorBody = new ExpressionReplacingVisitor(navigationExpansionExpression.State.CurrentParameter, result.Parameter).Visit(navigationExpansionExpression.State.PendingSelector.Body);
                    pendingSelector = Expression.Lambda(pendingSelectorBody, result.Parameter);
                }

                var newState = new NavigationExpansionExpressionState(
                    result.Parameter,
                    navigationExpansionExpression.State.SourceMappings,
                    pendingSelector,
                    applyPendingSelector: true,
                    navigationExpansionExpression.State.PendingOrderings,
                    navigationExpansionExpression.State.PendingIncludeChain,
                    navigationExpansionExpression.State.PendingCardinalityReducingOperator,
                    navigationExpansionExpression.State.CustomRootMappings,
                    navigationExpansionExpression.State.MaterializeCollectionNavigation);

                return(Operand : result.Source, newState);
            }

            return(navigationExpansionExpression.Operand, navigationExpansionExpression.State);
        }
        protected override Expression VisitExtension(Expression extensionExpression)
        {
            switch (extensionExpression)
            {
            case NavigationBindingExpression navigationBindingExpression:
                return(navigationBindingExpression.RootParameter.BuildPropertyAccess(
                           navigationBindingExpression.NavigationTreeNode.ToMapping));

            case NavigationExpansionRootExpression navigationExpansionRootExpression:
                return(Visit(navigationExpansionRootExpression.Unwrap()));

            case NavigationExpansionExpression navigationExpansionExpression:
            {
                var(result, state) = ApplyIncludes(navigationExpansionExpression);
                result             = Visit(result);

                if (!state.ApplyPendingSelector &&
                    state.PendingOrderings.Count == 0 &&
                    state.PendingCardinalityReducingOperator == null &&
                    state.MaterializeCollectionNavigation == null)
                {
                    return(result);
                }

                var parameterType = result.Type.GetSequenceType();

                foreach (var pendingOrdering in state.PendingOrderings)
                {
                    var remappedKeySelectorBody = new ExpressionReplacingVisitor(pendingOrdering.keySelector.Parameters[0], state.CurrentParameter).Visit(pendingOrdering.keySelector.Body);
                    var newSelectorBody         = new NavigationPropertyUnbindingVisitor(state.CurrentParameter).Visit(remappedKeySelectorBody);
                    var newSelector             = Expression.Lambda(newSelectorBody, state.CurrentParameter);
                    var orderingMethod          = pendingOrdering.method.MakeGenericMethod(state.CurrentParameter.Type, newSelectorBody.Type);
                    result = Expression.Call(orderingMethod, result, newSelector);
                }

                if (state.ApplyPendingSelector)
                {
                    var pendingSelector         = (LambdaExpression) new NavigationPropertyUnbindingVisitor(state.CurrentParameter).Visit(state.PendingSelector);
                    var pendingSelectorBodyType = pendingSelector.Type.GetGenericArguments()[1];

                    var pendingSelectMethod = result.Type.IsGenericType && (result.Type.GetGenericTypeDefinition() == typeof(IEnumerable <>) || result.Type.GetGenericTypeDefinition() == typeof(IOrderedEnumerable <>))
                                ? LinqMethodHelpers.EnumerableSelectMethodInfo.MakeGenericMethod(parameterType, pendingSelectorBodyType)
                                : LinqMethodHelpers.QueryableSelectMethodInfo.MakeGenericMethod(parameterType, pendingSelectorBodyType);

                    result        = Expression.Call(pendingSelectMethod, result, pendingSelector);
                    parameterType = result.Type.GetSequenceType();
                }

                if (state.PendingCardinalityReducingOperator != null)
                {
                    result = Expression.Call(state.PendingCardinalityReducingOperator, result);
                }

                if (state.MaterializeCollectionNavigation != null)
                {
                    result = new MaterializeCollectionNavigationExpression(result, state.MaterializeCollectionNavigation);
                }

                if (navigationExpansionExpression.Type != result.Type && navigationExpansionExpression.Type.IsGenericType)
                {
                    if (navigationExpansionExpression.Type.GetGenericTypeDefinition() == typeof(IOrderedQueryable <>))
                    {
                        var toOrderedQueryableMethodInfo = ToOrderedQueryableMethod.MakeGenericMethod(parameterType);

                        return(Expression.Call(toOrderedQueryableMethodInfo, result));
                    }

                    if (navigationExpansionExpression.Type.GetGenericTypeDefinition() == typeof(IOrderedEnumerable <>))
                    {
                        var toOrderedEnumerableMethodInfo = ToOrderedEnumerableMethod.MakeGenericMethod(parameterType);

                        return(Expression.Call(toOrderedEnumerableMethodInfo, result));
                    }
                }

                return(result);
            }
            }

            return(base.VisitExtension(extensionExpression));
        }