private (NavigationExpansionExpression navigationExpression, Expression nullKeyExpression) CreateNullComparisonArguments(
            NavigationBindingExpression navigationBindingExpression,
            NavigationExpansionExpression navigationExpansionExpression)
        {
            var navigationKeyAccessExpression = NavigationExpansionHelpers.CreateKeyAccessExpression(
                navigationBindingExpression,
                navigationBindingExpression.EntityType.FindPrimaryKey().Properties,
                addNullCheck: true);

            var nullKeyExpression = NavigationExpansionHelpers.CreateNullKeyExpression(
                navigationKeyAccessExpression.Type,
                navigationBindingExpression.EntityType.FindPrimaryKey().Properties.Count);

            var newNavigationExpansionExpressionState = new NavigationExpansionExpressionState(
                navigationExpansionExpression.State.CurrentParameter,
                navigationExpansionExpression.State.SourceMappings,
                Expression.Lambda(navigationKeyAccessExpression, navigationExpansionExpression.State.PendingSelector.Parameters[0]),
                applyPendingSelector: true,
                navigationExpansionExpression.State.PendingOrderings,
                navigationExpansionExpression.State.PendingIncludeChain,
                navigationExpansionExpression.State.PendingCardinalityReducingOperator,
                navigationExpansionExpression.State.PendingTags,
                navigationExpansionExpression.State.CustomRootMappings,
                navigationExpansionExpression.State.MaterializeCollectionNavigation);

            var navigationExpression = new NavigationExpansionExpression(
                navigationExpansionExpression.Operand,
                newNavigationExpansionExpressionState,
                navigationKeyAccessExpression.Type);

            return(navigationExpression, nullKeyExpression);
        }
Exemple #2
0
        private (NavigationExpansionExpression navigationExpression, Expression nullKeyExpression) CreateNullComparisonArguments(
            NavigationBindingExpression navigationBindingExpression,
            NavigationExpansionExpression navigationExpansionExpression)
        {
            var navigationKeyAccessExpression = NavigationExpansionHelpers.CreateKeyAccessExpression(
                navigationBindingExpression,
                new[] { navigationBindingExpression.EntityType.FindPrimaryKey().Properties.First() },
                addNullCheck: true);

            var nullKeyExpression = NavigationExpansionHelpers.CreateNullKeyExpression(
                navigationKeyAccessExpression.Type,
                keyCount: 1);

            var newNavigationExpansionExpressionState = new NavigationExpansionExpressionState(
                navigationExpansionExpression.State.CurrentParameter,
                navigationExpansionExpression.State.SourceMappings,
                Expression.Lambda(navigationKeyAccessExpression, navigationExpansionExpression.State.PendingSelector.Parameters[0]),
                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(navigationKeyAccessExpression.Type),
                navigationExpansionExpression.State.PendingTags,
                navigationExpansionExpression.State.CustomRootMappings,
                navigationExpansionExpression.State.MaterializeCollectionNavigation);

            var navigationExpression = new NavigationExpansionExpression(
                navigationExpansionExpression.Operand,
                newNavigationExpansionExpressionState,
                navigationKeyAccessExpression.Type);

            return(navigationExpression, nullKeyExpression);
        }
Exemple #3
0
        private (Expression operand, NavigationExpansionExpressionState state) ApplyIncludes(NavigationExpansionExpression navigationExpansionExpression)
        {
            var includeFinder = new PendingIncludeFindingVisitor();

            includeFinder.Visit(navigationExpansionExpression.State.PendingSelector.Body);

            var includeRewriter = new PendingSelectorIncludeRewriter();
            var rewrittenBody   = includeRewriter.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 (includeFinder.PendingIncludes.Count > 0)
            {
                var result = (source : navigationExpansionExpression.Operand, parameter : navigationExpansionExpression.State.CurrentParameter);
                foreach (var pendingIncludeNode in includeFinder.PendingIncludes)
                {
                    result = NavigationExpansionHelpers.AddNavigationJoin(
                        result.source,
                        result.parameter,
                        pendingIncludeNode.Value,
                        pendingIncludeNode.Key,
                        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.PendingTags,
                    navigationExpansionExpression.State.CustomRootMappings,
                    navigationExpansionExpression.State.MaterializeCollectionNavigation);

                return(operand : result.source, state : newState);
            }

            return(operand : navigationExpansionExpression.Operand, state : navigationExpansionExpression.State);
        }
Exemple #4
0
        protected override Expression VisitConstant(ConstantExpression constantExpression)
        {
            if (constantExpression.Type == typeof(AnonymousObject))
            {
                var anonymousObjectValue = (AnonymousObject)constantExpression.Value;

                if (anonymousObjectValue.OnlyNullValues(out var count))
                {
                    return(NavigationExpansionHelpers.CreateNullKeyExpression(typeof(AnonymousObject), count));
                }
            }

            return(base.VisitConstant(constantExpression));
        }
        public static Expression CreateCollectionNavigationExpression(
            NavigationTreeNode navigationTreeNode, ParameterExpression rootParameter, SourceMapping sourceMapping)
        {
            var collectionEntityType = navigationTreeNode.Navigation.ForeignKey.DeclaringEntityType;
            var entityQueryable      = (Expression)NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(collectionEntityType.ClrType);

            var outerBinding = new NavigationBindingExpression(
                rootParameter,
                navigationTreeNode.Parent,
                navigationTreeNode.Navigation.DeclaringEntityType,
                sourceMapping,
                navigationTreeNode.Navigation.DeclaringEntityType.ClrType);

            var outerKeyAccess = NavigationExpansionHelpers.CreateKeyAccessExpression(
                outerBinding,
                navigationTreeNode.Navigation.ForeignKey.PrincipalKey.Properties,
                addNullCheck: outerBinding.NavigationTreeNode.Optional);

            var collectionCurrentParameter = Expression.Parameter(collectionEntityType.ClrType, collectionEntityType.ClrType.GenerateParameterName());

            var innerKeyAccess = NavigationExpansionHelpers.CreateKeyAccessExpression(
                collectionCurrentParameter,
                navigationTreeNode.Navigation.ForeignKey.Properties);

            var predicate = Expression.Lambda(
                CreateKeyComparisonExpressionForCollectionNavigationSubquery(
                    outerKeyAccess,
                    innerKeyAccess,
                    outerBinding),
                collectionCurrentParameter);

            var operand = Expression.Call(
                LinqMethodHelpers.QueryableWhereMethodInfo.MakeGenericMethod(collectionEntityType.ClrType),
                entityQueryable,
                predicate);

            var result = NavigationExpansionHelpers.CreateNavigationExpansionRoot(operand, collectionEntityType, navigationTreeNode.Navigation);

            // this is needed for cases like: root.Include(r => r.Collection).ThenInclude(c => c.Reference).Select(r => r.Collection)
            // result should be elements of the collection navigation with their 'Reference' included
            var newSourceMapping = result.State.SourceMappings.Single();

            IncludeHelpers.CopyIncludeInformation(navigationTreeNode, newSourceMapping.NavigationTree, newSourceMapping);

            return(result);
        }
 protected override Expression VisitExtension(Expression extensionExpression)
 {
     if (extensionExpression is NavigationBindingExpression navigationBindingExpression)
     {
         if (navigationBindingExpression.RootParameter == _sourceParameter &&
             navigationBindingExpression.NavigationTreeNode.Parent != null &&
             navigationBindingExpression.NavigationTreeNode.Navigation is INavigation lastNavigation &&
             lastNavigation.IsCollection())
         {
             return(lastNavigation.ForeignKey.IsOwnership
                 ? NavigationExpansionHelpers.CreateNavigationExpansionRoot(navigationBindingExpression, lastNavigation.GetTargetType(), lastNavigation)
                 : CreateCollectionNavigationExpression(navigationBindingExpression.NavigationTreeNode, navigationBindingExpression.RootParameter, navigationBindingExpression.SourceMapping));
         }
         else
         {
             return(extensionExpression);
         }
     }
Exemple #7
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.PendingTags,
                    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 = ExpressionExtensions.CombineAndRemap(selector.Body, selectorParameter, navigationExpansionExpression.State.PendingSelector.Body);

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

            var boundSelectorBody = binder.Visit(remappedSelectorBody);

            if (boundSelectorBody is NavigationBindingExpression navigationBindingExpression &&
                navigationBindingExpression.NavigationTreeNode.Navigation is INavigation lastNavigation &&
                lastNavigation != null)
            {
                if (lastNavigation.IsCollection())
                {
                    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 = ExpressionExtensions.CombineAndRemap(innerKeyAccessLambda.Body, innerKeyAccessLambda.Parameters[0], navigationExpansionExpression.State.PendingSelector.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.PendingTags,
                        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.PendingTags,
                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);
        }
        private Expression TryRewriteNavigationComparison(Expression left, Expression right, bool equality)
        {
            var leftBinding       = left as NavigationBindingExpression;
            var rightBinding      = right as NavigationBindingExpression;
            var leftNullConstant  = left.IsNullConstantExpression();
            var rightNullConstant = right.IsNullConstantExpression();

            Expression newLeft  = null;
            Expression newRight = null;

            // comparing two different collection navigations always returns false
            if (leftBinding != null &&
                rightBinding != null &&
                leftBinding.NavigationTreeNode.Navigation != rightBinding.NavigationTreeNode.Navigation &&
                (leftBinding.NavigationTreeNode.IsCollection || rightBinding.NavigationTreeNode.IsCollection))
            {
                if (leftBinding.NavigationTreeNode.IsCollection)
                {
                    var parentTreeNode = leftBinding.NavigationTreeNode.Parent;
                    parentTreeNode.Children.Remove(leftBinding.NavigationTreeNode);
                }

                if (rightBinding.NavigationTreeNode.IsCollection)
                {
                    var parentTreeNode = rightBinding.NavigationTreeNode.Parent;
                    parentTreeNode.Children.Remove(rightBinding.NavigationTreeNode);
                }

                return(Expression.Constant(false));
            }

            if (leftBinding != null && rightBinding != null &&
                leftBinding.EntityType == rightBinding.EntityType)
            {
                if (leftBinding.NavigationTreeNode.Navigation == rightBinding.NavigationTreeNode.Navigation &&
                    leftBinding.NavigationTreeNode.IsCollection)
                {
                    leftBinding  = CreateParentBindingExpression(leftBinding);
                    rightBinding = CreateParentBindingExpression(rightBinding);
                }

                // TODO: what about entities without PKs?
                var primaryKeyProperties = leftBinding.EntityType.FindPrimaryKey().Properties;
                newLeft  = NavigationExpansionHelpers.CreateKeyAccessExpression(leftBinding, primaryKeyProperties, addNullCheck: leftBinding.NavigationTreeNode.Optional);
                newRight = NavigationExpansionHelpers.CreateKeyAccessExpression(rightBinding, primaryKeyProperties, addNullCheck: rightBinding.NavigationTreeNode.Optional);
            }

            if (leftBinding != null &&
                rightNullConstant)
            {
                if (leftBinding.NavigationTreeNode.IsCollection)
                {
                    leftBinding = CreateParentBindingExpression(leftBinding);
                }

                // TODO: what about entities without PKs?
                var primaryKeyProperties = leftBinding.EntityType.FindPrimaryKey().Properties;
                newLeft  = NavigationExpansionHelpers.CreateKeyAccessExpression(leftBinding, primaryKeyProperties, addNullCheck: leftBinding.NavigationTreeNode.Optional);
                newRight = NavigationExpansionHelpers.CreateNullKeyExpression(newLeft.Type, primaryKeyProperties.Count);
            }

            if (rightBinding != null &&
                leftNullConstant)
            {
                if (rightBinding.NavigationTreeNode.IsCollection)
                {
                    rightBinding = CreateParentBindingExpression(rightBinding);
                }

                // TODO: what about entities without PKs?
                var primaryKeyProperties = rightBinding.EntityType.FindPrimaryKey().Properties;
                newRight = NavigationExpansionHelpers.CreateKeyAccessExpression(rightBinding, primaryKeyProperties, addNullCheck: rightBinding.NavigationTreeNode.Optional);
                newLeft  = NavigationExpansionHelpers.CreateNullKeyExpression(newRight.Type, primaryKeyProperties.Count);
            }

            if (newLeft == null || newRight == null)
            {
                return(null);
            }

            if (newLeft.Type != newRight.Type)
            {
                if (newLeft.Type.IsNullableType())
                {
                    newRight = Expression.Convert(newRight, newLeft.Type);
                }
                else
                {
                    newLeft = Expression.Convert(newLeft, newRight.Type);
                }
            }

            return(equality
                ? Expression.Equal(newLeft, newRight)
                : Expression.NotEqual(newLeft, newRight));
        }