Ejemplo n.º 1
0
        protected override Expression VisitExtension(Expression extensionExpression)
        {
            switch (extensionExpression)
            {
            case NavigationBindingExpression navigationBindingExpression
                when navigationBindingExpression.RootParameter == _rootParameter:
            {
                var node        = navigationBindingExpression.NavigationTreeNode;
                var navigations = new List <INavigation>();
                while (node != null)
                {
                    if (node.Navigation != null)
                    {
                        navigations.Add(node.Navigation);
                    }
                    node = node.Parent;
                }

                var result = navigationBindingExpression.RootParameter.BuildPropertyAccess(
                    navigationBindingExpression.NavigationTreeNode.ToMapping,
                    navigations.Count == navigationBindingExpression.NavigationTreeNode.ToMapping.Count ? navigations : null);

                return(result.Type != navigationBindingExpression.Type
                        ? Expression.Convert(result, navigationBindingExpression.Type)
                        : result);
            }

            case CustomRootExpression customRootExpression
                when customRootExpression.RootParameter == _rootParameter:
            {
                var result = _rootParameter.BuildPropertyAccess(customRootExpression.Mapping);

                return(result.Type != customRootExpression.Type
                        ? Expression.Convert(result, customRootExpression.Type)
                        : result);
            }

            case NavigationExpansionRootExpression _:
            case NavigationExpansionExpression _:
            {
                var result = new NavigationExpansionReducingVisitor().Visit(extensionExpression);

                return(Visit(result));
            }

            default:
                return(base.VisitExtension(extensionExpression));
            }
        }
Ejemplo n.º 2
0
        private Expression ProcessMemberPushdown(
            Expression source,
            NavigationExpansionExpression navigationExpansionExpression,
            bool efProperty,
            MemberInfo memberInfo,
            string propertyName,
            Type resultType)
        {
            // in case of nested FirstOrDefaults, we need to dig into the inner most - that's where the member finally gets pushed down to
            if (navigationExpansionExpression.State.PendingSelector.Body is NavigationExpansionExpression navigationExpansionPendingSelector &&
                navigationExpansionPendingSelector.State.PendingCardinalityReducingOperator != null)
            {
                var newPendingSelector = (NavigationExpansionExpression)ProcessMemberPushdown(source, navigationExpansionPendingSelector, efProperty, memberInfo, propertyName, resultType);

                var newStateNested = new NavigationExpansionExpressionState(
                    navigationExpansionExpression.State.CurrentParameter,
                    navigationExpansionExpression.State.SourceMappings,
                    Expression.Lambda(newPendingSelector, navigationExpansionExpression.State.CurrentParameter),
                    applyPendingSelector: true,
                    navigationExpansionExpression.State.PendingOrderings,
                    navigationExpansionExpression.State.PendingIncludeChain,
                    // we need to remap cardinality reducing operator since it's source type has now changed
                    navigationExpansionExpression.State.PendingCardinalityReducingOperator.GetGenericMethodDefinition().MakeGenericMethod(newPendingSelector.Type),
                    navigationExpansionExpression.State.CustomRootMappings,
                    navigationExpansionExpression.State.MaterializeCollectionNavigation);

                return(new NavigationExpansionExpression(
                           navigationExpansionExpression.Operand,
                           newStateNested,
                           resultType));
            }

            var selectorParameter = Expression.Parameter(source.Type, navigationExpansionExpression.State.CurrentParameter.Name);

            var selectorBody = efProperty
                ? (Expression)Expression.Call(EF.PropertyMethod.MakeGenericMethod(resultType),
                                              selectorParameter,
                                              Expression.Constant(propertyName))
                : Expression.MakeMemberAccess(selectorParameter, memberInfo);

            if (navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.QueryableFirstOrDefaultMethodInfo) ||
                navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.QueryableFirstOrDefaultPredicateMethodInfo) ||
                navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.QueryableSingleOrDefaultMethodInfo) ||
                navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.QueryableSingleOrDefaultPredicateMethodInfo) ||
                navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.EnumerableFirstOrDefaultMethodInfo) ||
                navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.EnumerableFirstOrDefaultPredicateMethodInfo) ||
                navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.EnumerableSingleOrDefaultMethodInfo) ||
                navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.EnumerableSingleOrDefaultPredicateMethodInfo))
            {
                if (!selectorBody.Type.IsNullableType())
                {
                    selectorBody = Expression.Convert(selectorBody, selectorBody.Type.MakeNullable());
                }
            }

            var selector             = Expression.Lambda(selectorBody, selectorParameter);
            var remappedSelectorBody = ReplacingExpressionVisitor.Replace(
                selectorParameter, navigationExpansionExpression.State.PendingSelector.Body, selector.Body);

            var binder = new NavigationPropertyBindingVisitor(
                navigationExpansionExpression.State.CurrentParameter,
                navigationExpansionExpression.State.SourceMappings);

            var boundSelectorBody = binder.Visit(remappedSelectorBody);

            if (boundSelectorBody is NavigationBindingExpression navigationBindingExpression &&
                navigationBindingExpression.NavigationTreeNode.Navigation != null)
            {
                if (navigationBindingExpression.NavigationTreeNode.IsCollection)
                {
                    var lastNavigation = navigationBindingExpression.NavigationTreeNode.Navigation;
                    var collectionNavigationElementType = lastNavigation.ForeignKey.DeclaringEntityType.ClrType;
                    var entityQueryable = NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(collectionNavigationElementType);
                    var outerParameter  = Expression.Parameter(collectionNavigationElementType, collectionNavigationElementType.GenerateParameterName());

                    var outerKeyAccess = NavigationExpansionHelpers.CreateKeyAccessExpression(
                        outerParameter,
                        lastNavigation.ForeignKey.Properties);

                    var innerParameter       = Expression.Parameter(navigationExpansionExpression.Type);
                    var innerKeyAccessLambda = Expression.Lambda(
                        NavigationExpansionHelpers.CreateKeyAccessExpression(
                            innerParameter,
                            lastNavigation.ForeignKey.PrincipalKey.Properties),
                        innerParameter);

                    var combinedKeySelectorBody = ReplacingExpressionVisitor.Replace(
                        innerKeyAccessLambda.Parameters[0], navigationExpansionExpression.State.PendingSelector.Body, innerKeyAccessLambda.Body);
                    if (outerKeyAccess.Type != combinedKeySelectorBody.Type)
                    {
                        if (combinedKeySelectorBody.Type.IsNullableType())
                        {
                            outerKeyAccess = Expression.Convert(outerKeyAccess, combinedKeySelectorBody.Type);
                        }
                        else
                        {
                            combinedKeySelectorBody = Expression.Convert(combinedKeySelectorBody, outerKeyAccess.Type);
                        }
                    }

                    var rewrittenState = new NavigationExpansionExpressionState(
                        navigationExpansionExpression.State.CurrentParameter,
                        navigationExpansionExpression.State.SourceMappings,
                        Expression.Lambda(combinedKeySelectorBody, navigationExpansionExpression.State.CurrentParameter),
                        applyPendingSelector: true,
                        navigationExpansionExpression.State.PendingOrderings,
                        navigationExpansionExpression.State.PendingIncludeChain,
                        // we need to remap cardinality reducing operator since it's source type has now changed
                        navigationExpansionExpression.State.PendingCardinalityReducingOperator.GetGenericMethodDefinition().MakeGenericMethod(combinedKeySelectorBody.Type),
                        navigationExpansionExpression.State.CustomRootMappings,
                        materializeCollectionNavigation: null);

                    var rewrittenNavigationExpansionExpression = new NavigationExpansionExpression(navigationExpansionExpression.Operand, rewrittenState, combinedKeySelectorBody.Type);
                    var inner = new NavigationExpansionReducingVisitor().Visit(rewrittenNavigationExpansionExpression);

                    var predicate = Expression.Lambda(
                        Expression.Equal(outerKeyAccess, inner),
                        outerParameter);

                    var whereMethodInfo = LinqMethodHelpers.QueryableWhereMethodInfo.MakeGenericMethod(collectionNavigationElementType);
                    var rewritten       = Expression.Call(
                        whereMethodInfo,
                        entityQueryable,
                        predicate);

                    var entityType = lastNavigation.ForeignKey.DeclaringEntityType;

                    return(NavigationExpansionHelpers.CreateNavigationExpansionRoot(rewritten, entityType, materializeCollectionNavigation: null));
                }
                else
                {
                    return(ProcessSelectCore(
                               navigationExpansionExpression.Operand,
                               navigationExpansionExpression.State,
                               selector,
                               selectorBody.Type));
                }
            }

            var newState = new NavigationExpansionExpressionState(
                navigationExpansionExpression.State.CurrentParameter,
                navigationExpansionExpression.State.SourceMappings,
                Expression.Lambda(boundSelectorBody, navigationExpansionExpression.State.CurrentParameter),
                applyPendingSelector: true,
                navigationExpansionExpression.State.PendingOrderings,
                navigationExpansionExpression.State.PendingIncludeChain,
                // we need to remap cardinality reducing operator since it's source type has now changed
                navigationExpansionExpression.State.PendingCardinalityReducingOperator.GetGenericMethodDefinition().MakeGenericMethod(boundSelectorBody.Type),
                navigationExpansionExpression.State.CustomRootMappings,
                navigationExpansionExpression.State.MaterializeCollectionNavigation);

            // TODO: expand navigations

            var result = new NavigationExpansionExpression(
                navigationExpansionExpression.Operand,
                newState,
                selectorBody.Type);

            return(resultType != result.Type
                ? (Expression)Expression.Convert(result, resultType)
                : result);
        }