示例#1
0
            private List <EntityShaperExpression> GetNestedShapers(IEntityType entityType, Expression valueBufferExpression)
            {
                var nestedEntities = new List <EntityShaperExpression>();

                foreach (var ownedNavigation in entityType.GetNavigations().Concat(entityType.GetDerivedNavigations()))
                {
                    var fk = ownedNavigation.ForeignKey;
                    if (!fk.IsOwnership ||
                        ownedNavigation.IsDependentToPrincipal() ||
                        fk.DeclaringEntityType.IsDocumentRoot())
                    {
                        continue;
                    }

                    var targetType = ownedNavigation.GetTargetType();

                    var nestedValueBufferExpression = _shapedQueryCompilingExpressionVisitor.CreateReadValueExpression(
                        valueBufferExpression,
                        valueBufferExpression.Type,
                        ownedNavigation.GetIndex(),
                        ownedNavigation);

                    var nestedShaper = new EntityShaperExpression(
                        targetType, nestedValueBufferExpression, nullable: true, ownedNavigation,
                        GetNestedShapers(targetType, nestedValueBufferExpression));
                    nestedEntities.Add(nestedShaper);
                }

                return(nestedEntities.Count == 0 ? null : nestedEntities);
            }
        private SqlExpression BindProperty(EntityShaperExpression entityShaperExpression, IProperty property)
        {
            var projectionBindingExpression = (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression;

            return(((SelectExpression)projectionBindingExpression.QueryExpression)
                   .BindProperty(projectionBindingExpression, property));
        }
示例#3
0
        private Expression BindProperty(EntityShaperExpression entityShaper, string propertyName)
        {
            var property         = entityShaper.EntityType.FindProperty(propertyName);
            var selectExpression = (SelectExpression)entityShaper.ValueBufferExpression.QueryExpression;

            return(selectExpression.BindProperty(entityShaper.ValueBufferExpression, property));
        }
 public InMemoryShapedQueryExpression(IEntityType entityType)
 {
     QueryExpression  = new InMemoryQueryExpression(entityType);
     ShaperExpression = new EntityShaperExpression(
         entityType,
         new ProjectionBindingExpression(new ProjectionMember(), typeof(ValueBuffer)),
         false);
 }
 public RelationalShapedQueryExpression(IEntityType entityType, string sql)
 {
     QueryExpression  = new SelectExpression(entityType, sql);
     ShaperExpression = new EntityShaperExpression(
         entityType,
         new ProjectionBindingExpression(
             QueryExpression,
             new ProjectionMember(),
             typeof(ValueBuffer)),
         false);
 }
        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        public virtual void AddNavigationBinding([NotNull] INavigation navigation, [NotNull] EntityShaperExpression entityShaper)
        {
            if (!EntityType.IsAssignableFrom(navigation.DeclaringEntityType) &&
                !navigation.DeclaringEntityType.IsAssignableFrom(EntityType))
            {
                throw new InvalidOperationException(
                          InMemoryStrings.UnableToBindMemberToEntityProjection("Navigation", navigation.Name, EntityType.DisplayName()));
            }

            _navigationExpressionsCache[navigation] = entityShaper;
        }
示例#7
0
        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        public virtual void AddNavigationBinding([NotNull] INavigation navigation, [NotNull] EntityShaperExpression entityShaper)
        {
            if (!EntityType.IsAssignableFrom(navigation.DeclaringEntityType) &&
                !navigation.DeclaringEntityType.IsAssignableFrom(EntityType))
            {
                throw new InvalidOperationException(
                          CoreStrings.EntityProjectionExpressionCalledWithIncorrectInterface(
                              "AddNavigationBinding", nameof(INavigation), EntityType.DisplayName(), $"Property:{navigation.Name}"));
            }

            _navigationExpressionsCache[navigation] = entityShaper;
        }
        public virtual void AddNavigationBinding(INavigation navigation, EntityShaperExpression entityShaper)
        {
            if (!EntityType.IsAssignableFrom(navigation.DeclaringEntityType) &&
                !navigation.DeclaringEntityType.IsAssignableFrom(EntityType))
            {
                throw new InvalidOperationException(
                          $"Called EntityProjectionExpression.AddNavigationBinding() with incorrect INavigation. " +
                          $"EntityType:{EntityType.DisplayName()}, Property:{navigation.Name}");
            }

            _navigationExpressionsCache[navigation] = entityShaper;
        }
示例#9
0
        protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
        {
            if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var memberName))
            {
                if (!_clientEval)
                {
                    return(null);
                }

                var visitedSource = Visit(source);

                EntityShaperExpression shaperExpression;
                switch (visitedSource)
                {
                case EntityShaperExpression shaper:
                    shaperExpression = shaper;
                    break;

                case UnaryExpression unaryExpression:
                    shaperExpression = unaryExpression.Operand as EntityShaperExpression;
                    if (shaperExpression == null ||
                        unaryExpression.NodeType != ExpressionType.Convert)
                    {
                        return(null);
                    }

                    break;

                case ParameterExpression parameterExpression:
                    if (!_collectionShaperMapping.TryGetValue(parameterExpression, out var collectionShaper))
                    {
                        return(null);
                    }

                    shaperExpression = (EntityShaperExpression)collectionShaper.InnerShaper;
                    break;

                default:
                    return(null);
                }

                EntityProjectionExpression innerEntityProjection;
                switch (shaperExpression.ValueBufferExpression)
                {
                case ProjectionBindingExpression innerProjectionBindingExpression:
                    innerEntityProjection = (EntityProjectionExpression)_selectExpression.Projection[
                        innerProjectionBindingExpression.Index.Value].Expression;
                    break;

                case UnaryExpression unaryExpression:
                    innerEntityProjection = (EntityProjectionExpression)((UnaryExpression)unaryExpression.Operand).Operand;
                    break;

                default:
                    throw new InvalidOperationException(CoreStrings.QueryFailed(methodCallExpression.Print(), GetType().Name));
                }

                Expression navigationProjection;
                var        navigation = _includedNavigations.FirstOrDefault(n => n.Name == memberName);
                if (navigation == null)
                {
                    navigationProjection = innerEntityProjection.BindMember(
                        memberName, visitedSource.Type, clientEval: true, out var propertyBase);

                    if (!(propertyBase is INavigation projectedNavigation) ||
                        !projectedNavigation.IsEmbedded())
                    {
                        return(null);
                    }

                    navigation = projectedNavigation;
                }
                else
                {
                    navigationProjection = innerEntityProjection.BindNavigation(navigation, clientEval: true);
                }

                switch (navigationProjection)
                {
                case EntityProjectionExpression entityProjection:
                    return(new EntityShaperExpression(
                               navigation.GetTargetType(),
                               Expression.Convert(Expression.Convert(entityProjection, typeof(object)), typeof(ValueBuffer)),
                               nullable: true));

                case ObjectArrayProjectionExpression objectArrayProjectionExpression:
                {
                    var innerShaperExpression = new EntityShaperExpression(
                        navigation.GetTargetType(),
                        Expression.Convert(
                            Expression.Convert(objectArrayProjectionExpression.InnerProjection, typeof(object)), typeof(ValueBuffer)),
                        nullable: true);

                    return(new CollectionShaperExpression(
                               objectArrayProjectionExpression,
                               innerShaperExpression,
                               navigation,
                               innerShaperExpression.EntityType.ClrType));
                }

                default:
                    throw new InvalidOperationException(CoreStrings.QueryFailed(methodCallExpression.Print(), GetType().Name));
                }
            }

            if (_clientEval)
            {
                var method = methodCallExpression.Method;
                if (method.DeclaringType == typeof(Queryable))
                {
                    var genericMethod = method.IsGenericMethod ? method.GetGenericMethodDefinition() : null;
                    var visitedSource = Visit(methodCallExpression.Arguments[0]);

                    switch (method.Name)
                    {
                    case nameof(Queryable.AsQueryable)
                        when genericMethod == QueryableMethods.AsQueryable:
                        // Unwrap AsQueryable
                        return(visitedSource);

                    case nameof(Queryable.Select)
                        when genericMethod == QueryableMethods.Select:
                        if (!(visitedSource is CollectionShaperExpression shaper))
                        {
                            return(null);
                        }

                        var lambda = methodCallExpression.Arguments[1].UnwrapLambdaFromQuote();

                        _collectionShaperMapping.Add(lambda.Parameters.Single(), shaper);

                        lambda = Expression.Lambda(Visit(lambda.Body), lambda.Parameters);
                        return(Expression.Call(
                                   EnumerableMethods.Select.MakeGenericMethod(method.GetGenericArguments()),
                                   shaper,
                                   lambda));
                    }
                }
            }

            return(base.VisitMethodCall(methodCallExpression));
        }
示例#10
0
        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        protected override Expression VisitMember(MemberExpression memberExpression)
        {
            if (!_clientEval)
            {
                return(null);
            }

            var innerExpression = Visit(memberExpression.Expression);

            EntityShaperExpression shaperExpression;

            switch (innerExpression)
            {
            case EntityShaperExpression shaper:
                shaperExpression = shaper;
                break;

            case UnaryExpression unaryExpression:
                shaperExpression = unaryExpression.Operand as EntityShaperExpression;
                if (shaperExpression == null ||
                    unaryExpression.NodeType != ExpressionType.Convert)
                {
                    return(memberExpression.Update(innerExpression));
                }

                break;

            default:
                return(memberExpression.Update(innerExpression));
            }

            EntityProjectionExpression innerEntityProjection;

            switch (shaperExpression.ValueBufferExpression)
            {
            case ProjectionBindingExpression innerProjectionBindingExpression:
                innerEntityProjection = (EntityProjectionExpression)_selectExpression.Projection[
                    innerProjectionBindingExpression.Index.Value].Expression;
                break;

            case UnaryExpression unaryExpression:
                // Unwrap EntityProjectionExpression when the root entity is not projected
                innerEntityProjection = (EntityProjectionExpression)((UnaryExpression)unaryExpression.Operand).Operand;
                break;

            default:
                throw new InvalidOperationException(CoreStrings.QueryFailed(memberExpression.Print(), GetType().Name));
            }

            var navigationProjection = innerEntityProjection.BindMember(
                memberExpression.Member, innerExpression.Type, clientEval: true, out var propertyBase);

            if (!(propertyBase is INavigation navigation) ||
                !navigation.IsEmbedded())
            {
                return(memberExpression.Update(innerExpression));
            }

            switch (navigationProjection)
            {
            case EntityProjectionExpression entityProjection:
                return(new EntityShaperExpression(
                           navigation.GetTargetType(),
                           Expression.Convert(Expression.Convert(entityProjection, typeof(object)), typeof(ValueBuffer)),
                           nullable: true));

            case ObjectArrayProjectionExpression objectArrayProjectionExpression:
            {
                var innerShaperExpression = new EntityShaperExpression(
                    navigation.GetTargetType(),
                    Expression.Convert(
                        Expression.Convert(objectArrayProjectionExpression.InnerProjection, typeof(object)), typeof(ValueBuffer)),
                    nullable: true);

                return(new CollectionShaperExpression(
                           objectArrayProjectionExpression,
                           innerShaperExpression,
                           navigation,
                           innerShaperExpression.EntityType.ClrType));
            }

            default:
                throw new InvalidOperationException(CoreStrings.QueryFailed(memberExpression.Print(), GetType().Name));
            }
        }
示例#11
0
        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        protected override Expression VisitMember(MemberExpression memberExpression)
        {
            Check.NotNull(memberExpression, nameof(memberExpression));

            if (!_clientEval)
            {
                return(null);
            }

            var innerExpression = Visit(memberExpression.Expression);

            EntityShaperExpression shaperExpression;

            switch (innerExpression)
            {
            case EntityShaperExpression shaper:
                shaperExpression = shaper;
                break;

            case UnaryExpression unaryExpression:
                shaperExpression = unaryExpression.Operand as EntityShaperExpression;
                if (shaperExpression == null ||
                    unaryExpression.NodeType != ExpressionType.Convert)
                {
                    return(NullSafeUpdate(innerExpression));
                }

                break;

            default:
                return(NullSafeUpdate(innerExpression));
            }

            EntityProjectionExpression innerEntityProjection;

            switch (shaperExpression.ValueBufferExpression)
            {
            case ProjectionBindingExpression innerProjectionBindingExpression:
                innerEntityProjection = (EntityProjectionExpression)_selectExpression.Projection[
                    innerProjectionBindingExpression.Index.Value].Expression;
                break;

            case UnaryExpression unaryExpression:
                // Unwrap EntityProjectionExpression when the root entity is not projected
                innerEntityProjection = (EntityProjectionExpression)((UnaryExpression)unaryExpression.Operand).Operand;
                break;

            default:
                throw new InvalidOperationException(CoreStrings.TranslationFailed(memberExpression.Print()));
            }

            var navigationProjection = innerEntityProjection.BindMember(
                memberExpression.Member, innerExpression.Type, clientEval: true, out var propertyBase);

            if (!(propertyBase is INavigation navigation) ||
                !navigation.IsEmbedded())
            {
                return(NullSafeUpdate(innerExpression));
            }

            switch (navigationProjection)
            {
            case EntityProjectionExpression entityProjection:
                return(new EntityShaperExpression(
                           navigation.TargetEntityType,
                           Expression.Convert(Expression.Convert(entityProjection, typeof(object)), typeof(ValueBuffer)),
                           nullable: true));

            case ObjectArrayProjectionExpression objectArrayProjectionExpression:
            {
                var innerShaperExpression = new EntityShaperExpression(
                    navigation.TargetEntityType,
                    Expression.Convert(
                        Expression.Convert(objectArrayProjectionExpression.InnerProjection, typeof(object)), typeof(ValueBuffer)),
                    nullable: true);

                return(new CollectionShaperExpression(
                           objectArrayProjectionExpression,
                           innerShaperExpression,
                           navigation,
                           innerShaperExpression.EntityType.ClrType));
            }

            default:
                throw new InvalidOperationException(CoreStrings.TranslationFailed(memberExpression.Print()));
            }

            Expression NullSafeUpdate(Expression expression)
            {
                Expression updatedMemberExpression = memberExpression.Update(
                    expression != null ? MatchTypes(expression, memberExpression.Expression.Type) : expression);

                if (expression?.Type.IsNullableType() == true)
                {
                    var nullableReturnType = memberExpression.Type.MakeNullable();
                    if (!memberExpression.Type.IsNullableType())
                    {
                        updatedMemberExpression = Expression.Convert(updatedMemberExpression, nullableReturnType);
                    }

                    updatedMemberExpression = Expression.Condition(
                        Expression.Equal(expression, Expression.Default(expression.Type)),
                        Expression.Constant(null, nullableReturnType),
                        updatedMemberExpression);
                }

                return(updatedMemberExpression);
            }
        }
 private SqlExpression BindProperty(EntityShaperExpression entityShaper, IProperty property)
 {
     return(((SelectExpression)entityShaper.ValueBufferExpression.QueryExpression)
            .BindProperty(entityShaper.ValueBufferExpression, property));
 }
        protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
        {
            if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var memberName))
            {
                if (!_clientEval)
                {
                    return(null);
                }

                var visitedSource = Visit(source);

                EntityShaperExpression shaperExpression;
                switch (visitedSource)
                {
                case EntityShaperExpression shaper:
                    shaperExpression = shaper;
                    break;

                case UnaryExpression unaryExpression:
                    shaperExpression = unaryExpression.Operand as EntityShaperExpression;
                    if (shaperExpression == null)
                    {
                        return(null);
                    }
                    break;

                default:
                    return(null);
                }

                EntityProjectionExpression innerEntityProjection;
                switch (shaperExpression.ValueBufferExpression)
                {
                case ProjectionBindingExpression innerProjectionBindingExpression:
                    innerEntityProjection = (EntityProjectionExpression)_selectExpression.Projection[
                        innerProjectionBindingExpression.Index.Value].Expression;
                    break;

                case UnaryExpression unaryExpression:
                    innerEntityProjection = (EntityProjectionExpression)((UnaryExpression)unaryExpression.Operand).Operand;
                    break;

                default:
                    throw new InvalidOperationException();
                }

                var navigationProjection = innerEntityProjection.BindMember(
                    memberName, visitedSource.Type, clientEval: true, out var propertyBase);

                if (!(propertyBase is INavigation navigation) ||
                    !navigation.IsEmbedded())
                {
                    return(null);
                }

                switch (navigationProjection)
                {
                case EntityProjectionExpression entityProjection:
                    return(new EntityShaperExpression(
                               navigation.GetTargetType(),
                               Expression.Convert(Expression.Convert(entityProjection, typeof(object)), typeof(ValueBuffer)),
                               nullable: true));

                case ObjectArrayProjectionExpression objectArrayProjectionExpression:
                {
                    var innerShaperExpression = new EntityShaperExpression(
                        navigation.GetTargetType(),
                        Expression.Convert(
                            Expression.Convert(objectArrayProjectionExpression.InnerProjection, typeof(object)), typeof(ValueBuffer)),
                        nullable: true);

                    return(new CollectionShaperExpression(
                               objectArrayProjectionExpression,
                               innerShaperExpression,
                               navigation,
                               innerShaperExpression.EntityType.ClrType));
                }

                default:
                    throw new InvalidOperationException();
                }
            }

            return(base.VisitMethodCall(methodCallExpression));
        }
示例#14
0
        public virtual EntityShaperExpression AddNavigationToWeakEntityType(
            EntityProjectionExpression entityProjectionExpression,
            INavigation navigation,
            InMemoryQueryExpression innerQueryExpression,
            LambdaExpression outerKeySelector,
            LambdaExpression innerKeySelector)
        {
            // GroupJoin phase
            var groupTransparentIdentifierType = TransparentIdentifierFactory.Create(
                typeof(ValueBuffer), typeof(IEnumerable <ValueBuffer>));
            var outerParameter  = Parameter(typeof(ValueBuffer), "outer");
            var innerParameter  = Parameter(typeof(IEnumerable <ValueBuffer>), "inner");
            var outerMemberInfo = groupTransparentIdentifierType.GetTypeInfo().GetDeclaredField("Outer");
            var innerMemberInfo = groupTransparentIdentifierType.GetTypeInfo().GetDeclaredField("Inner");
            var resultSelector  = Lambda(
                New(
                    groupTransparentIdentifierType.GetTypeInfo().DeclaredConstructors.Single(),
                    new[] { outerParameter, innerParameter },
                    new[] { outerMemberInfo, innerMemberInfo }),
                outerParameter,
                innerParameter);

            var groupJoinExpression = Call(
                InMemoryLinqOperatorProvider.GroupJoin.MakeGenericMethod(
                    typeof(ValueBuffer), typeof(ValueBuffer), outerKeySelector.ReturnType, groupTransparentIdentifierType),
                ServerQueryExpression,
                innerQueryExpression.ServerQueryExpression,
                outerKeySelector,
                innerKeySelector,
                resultSelector);

            // SelectMany phase
            var collectionParameter = Parameter(groupTransparentIdentifierType, "collection");
            var collection          = MakeMemberAccess(collectionParameter, innerMemberInfo);

            outerParameter = Parameter(groupTransparentIdentifierType, "outer");
            innerParameter = Parameter(typeof(ValueBuffer), "inner");

            var resultValueBufferExpressions = new List <Expression>();
            var projectionMapping            = new Dictionary <ProjectionMember, Expression>();
            var replacingVisitor             = new ReplacingExpressionVisitor(
                new Dictionary <Expression, Expression>
            {
                { CurrentParameter, MakeMemberAccess(outerParameter, outerMemberInfo) },
                { innerQueryExpression.CurrentParameter, innerParameter }
            });
            var index = 0;

            EntityProjectionExpression copyEntityProjectionToOuter(EntityProjectionExpression entityProjection)
            {
                var readExpressionMap = new Dictionary <IProperty, Expression>();

                foreach (var property in GetAllPropertiesInHierarchy(entityProjection.EntityType))
                {
                    var replacedExpression = replacingVisitor.Visit(entityProjection.BindProperty(property));
                    resultValueBufferExpressions.Add(replacedExpression);
                    readExpressionMap[property] = CreateReadValueExpression(replacedExpression.Type, index++, property);
                }

                var newEntityProjection = new EntityProjectionExpression(entityProjection.EntityType, readExpressionMap);

                if (ReferenceEquals(entityProjectionExpression, entityProjection))
                {
                    entityProjectionExpression = newEntityProjection;
                }

                // Also lift nested entity projections
                foreach (var navigation in entityProjection.EntityType.GetTypesInHierarchy()
                         .SelectMany(EntityTypeExtensions.GetDeclaredNavigations))
                {
                    var boundEntityShaperExpression = entityProjection.BindNavigation(navigation);
                    if (boundEntityShaperExpression != null)
                    {
                        var innerEntityProjection    = (EntityProjectionExpression)boundEntityShaperExpression.ValueBufferExpression;
                        var newInnerEntityProjection = copyEntityProjectionToOuter(innerEntityProjection);
                        boundEntityShaperExpression = boundEntityShaperExpression.Update(newInnerEntityProjection);
                        newEntityProjection.AddNavigationBinding(navigation, boundEntityShaperExpression);
                    }
                }

                return(newEntityProjection);
            }

            foreach (var projection in _projectionMapping)
            {
                if (projection.Value is EntityProjectionExpression entityProjection)
                {
                    projectionMapping[projection.Key] = copyEntityProjectionToOuter(entityProjection);
                }
                else
                {
                    var replacedExpression = replacingVisitor.Visit(projection.Value);
                    resultValueBufferExpressions.Add(replacedExpression);
                    projectionMapping[projection.Key]
                        = CreateReadValueExpression(replacedExpression.Type, index++, InferPropertyFromInner(projection.Value));
                }
            }

            _projectionMapping = projectionMapping;

            var outerIndex = index;
            var nullableReadValueExpressionVisitor = new NullableReadValueExpressionVisitor();
            var innerEntityProjection = (EntityProjectionExpression)innerQueryExpression.GetMappedProjection(new ProjectionMember());

            var innerReadExpressionMap = new Dictionary <IProperty, Expression>();

            foreach (var property in GetAllPropertiesInHierarchy(innerEntityProjection.EntityType))
            {
                var replacedExpression = replacingVisitor.Visit(innerEntityProjection.BindProperty(property));
                replacedExpression = nullableReadValueExpressionVisitor.Visit(replacedExpression);
                resultValueBufferExpressions.Add(replacedExpression);
                innerReadExpressionMap[property] = CreateReadValueExpression(replacedExpression.Type, index++, property);
            }
            innerEntityProjection = new EntityProjectionExpression(innerEntityProjection.EntityType, innerReadExpressionMap);

            var collectionSelector = Lambda(
                Call(
                    InMemoryLinqOperatorProvider.DefaultIfEmptyWithArgument.MakeGenericMethod(typeof(ValueBuffer)),
                    collection,
                    New(
                        _valueBufferConstructor,
                        NewArrayInit(
                            typeof(object),
                            Enumerable.Range(0, index - outerIndex).Select(i => Constant(null))))),
                collectionParameter);

            resultSelector = Lambda(
                New(
                    _valueBufferConstructor,
                    NewArrayInit(
                        typeof(object),
                        resultValueBufferExpressions
                        .Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e)
                        .ToArray())),
                outerParameter,
                innerParameter);

            ServerQueryExpression = Call(
                InMemoryLinqOperatorProvider.SelectManyWithCollectionSelector.MakeGenericMethod(
                    groupTransparentIdentifierType, typeof(ValueBuffer), typeof(ValueBuffer)),
                groupJoinExpression,
                collectionSelector,
                resultSelector);

            var entityShaper = new EntityShaperExpression(innerEntityProjection.EntityType, innerEntityProjection, nullable: true);

            entityProjectionExpression.AddNavigationBinding(navigation, entityShaper);

            return(entityShaper);
        }
示例#15
0
            private Expression TryExpand(Expression source, MemberIdentity member)
            {
                source = source.UnwrapTypeConversion(out var convertedType);
                if (!(source is EntityShaperExpression entityShaperExpression))
                {
                    return(null);
                }

                var entityType = entityShaperExpression.EntityType;

                if (convertedType != null)
                {
                    entityType = entityType.GetRootType().GetDerivedTypesInclusive()
                                 .FirstOrDefault(et => et.ClrType == convertedType);

                    if (entityType == null)
                    {
                        return(null);
                    }
                }

                var navigation = member.MemberInfo != null
                    ? entityType.FindNavigation(member.MemberInfo)
                    : entityType.FindNavigation(member.Name);

                if (navigation == null)
                {
                    return(null);
                }

                var targetEntityType = navigation.GetTargetType();

                if (targetEntityType == null ||
                    (!targetEntityType.HasDefiningNavigation() &&
                     !targetEntityType.IsOwned()))
                {
                    return(null);
                }

                var foreignKey = navigation.ForeignKey;

                if (navigation.IsCollection())
                {
                    var innerShapedQuery = CreateShapedQueryExpression(
                        targetEntityType, _sqlExpressionFactory.SelectWithCrossDb(targetEntityType));

                    var makeNullable = foreignKey.PrincipalKey.Properties
                                       .Concat(foreignKey.Properties)
                                       .Select(p => p.ClrType)
                                       .Any(t => t.IsNullableType());

                    var innerSequenceType             = innerShapedQuery.Type.TryGetSequenceType();
                    var correlationPredicateParameter = Expression.Parameter(innerSequenceType);

                    var outerKey = entityShaperExpression.CreateKeyAccessExpression(
                        navigation.IsDependentToPrincipal()
                            ? foreignKey.Properties
                            : foreignKey.PrincipalKey.Properties,
                        makeNullable);
                    var innerKey = correlationPredicateParameter.CreateKeyAccessExpression(
                        navigation.IsDependentToPrincipal()
                            ? foreignKey.PrincipalKey.Properties
                            : foreignKey.Properties,
                        makeNullable);

                    var outerKeyFirstProperty = outerKey is NewExpression newExpression
                        ? ((UnaryExpression)((NewArrayExpression)newExpression.Arguments[0]).Expressions[0]).Operand
                        : outerKey;

                    var predicate = outerKeyFirstProperty.Type.IsNullableType()
                        ? Expression.AndAlso(
                        Expression.NotEqual(outerKeyFirstProperty, Expression.Constant(null, outerKeyFirstProperty.Type)),
                        Expression.Equal(outerKey, innerKey))
                        : Expression.Equal(outerKey, innerKey);

                    var correlationPredicate = Expression.Lambda(predicate, correlationPredicateParameter);

                    return(Expression.Call(
                               QueryableMethods.Where.MakeGenericMethod(innerSequenceType),
                               innerShapedQuery,
                               Expression.Quote(correlationPredicate)));
                }

                var entityProjectionExpression = (EntityProjectionExpression)
                                                 (entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression
                        ? _selectExpression.GetMappedProjection(projectionBindingExpression.ProjectionMember)
                        : entityShaperExpression.ValueBufferExpression);

                var innerShaper = entityProjectionExpression.BindNavigation(navigation);

                if (innerShaper == null)
                {
                    var innerSelectExpression = _sqlExpressionFactory.SelectWithCrossDb(targetEntityType);
                    var innerShapedQuery      = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);

                    var makeNullable = foreignKey.PrincipalKey.Properties
                                       .Concat(foreignKey.Properties)
                                       .Select(p => p.ClrType)
                                       .Any(t => t.IsNullableType());

                    var outerKey = entityShaperExpression.CreateKeyAccessExpression(
                        navigation.IsDependentToPrincipal()
                            ? foreignKey.Properties
                            : foreignKey.PrincipalKey.Properties,
                        makeNullable);
                    var innerKey = innerShapedQuery.ShaperExpression.CreateKeyAccessExpression(
                        navigation.IsDependentToPrincipal()
                            ? foreignKey.PrincipalKey.Properties
                            : foreignKey.Properties,
                        makeNullable);

                    var joinPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey));
                    _selectExpression.AddLeftJoin(innerSelectExpression, joinPredicate, null);
#pragma warning disable CA1826 // Do not use Enumerable methods on indexable collections. Instead use the collection directly
                    var leftJoinTable = ((LeftJoinExpression)_selectExpression.Tables.Last()).Table;
#pragma warning restore CA1826 // Do not use Enumerable methods on indexable collections. Instead use the collection directly
                    innerShaper = new EntityShaperExpression(
                        targetEntityType,
                        new EntityProjectionExpression(targetEntityType, leftJoinTable, true),
                        true);
                    entityProjectionExpression.AddNavigationBinding(navigation, innerShaper);
                }

                return(innerShaper);
            }
            private Expression Expand(Expression source, MemberIdentity member)
            {
                Type convertedType = null;

                if (source is UnaryExpression unaryExpression &&
                    unaryExpression.NodeType == ExpressionType.Convert)
                {
                    source = unaryExpression.Operand;
                    if (unaryExpression.Type != typeof(object))
                    {
                        convertedType = unaryExpression.Type;
                    }
                }

                if (source is EntityShaperExpression entityShaperExpression)
                {
                    var entityType = entityShaperExpression.EntityType;
                    if (convertedType != null)
                    {
                        entityType = entityType.RootType().GetDerivedTypesInclusive()
                                     .FirstOrDefault(et => et.ClrType == convertedType);

                        if (entityType == null)
                        {
                            return(null);
                        }
                    }

                    var navigation = member.MemberInfo != null
                        ? entityType.FindNavigation(member.MemberInfo)
                        : entityType.FindNavigation(member.Name);

                    if (navigation != null)
                    {
                        if (navigation.IsCollection())
                        {
                            return(CreateShapedQueryExpression(
                                       navigation.GetTargetType(),
                                       _sqlExpressionFactory.Select(navigation.GetTargetType())));
                        }

                        var entityProjectionExpression = (EntityProjectionExpression)
                                                         (entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression
                                ? _selectExpression.GetMappedProjection(projectionBindingExpression.ProjectionMember)
                                : entityShaperExpression.ValueBufferExpression);

                        var innerShaper = entityProjectionExpression.BindNavigation(navigation);
                        if (innerShaper == null)
                        {
                            var targetEntityType      = navigation.GetTargetType();
                            var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType);
                            var innerShapedQuery      = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);

                            var makeNullable = navigation.ForeignKey.PrincipalKey.Properties
                                               .Concat(navigation.ForeignKey.Properties)
                                               .Select(p => p.ClrType)
                                               .Any(t => t.IsNullableType());

                            var outerKey = CreateKeyAccessExpression(
                                entityShaperExpression, navigation.ForeignKey.PrincipalKey.Properties, makeNullable);
                            var innerKey = CreateKeyAccessExpression(
                                innerShapedQuery.ShaperExpression, navigation.ForeignKey.Properties, makeNullable);

                            var joinPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey));
                            _selectExpression.AddLeftJoin(innerSelectExpression, joinPredicate, null);
                            var leftJoinTable = ((LeftJoinExpression)_selectExpression.Tables.Last()).Table;
                            innerShaper = new EntityShaperExpression(targetEntityType,
                                                                     new EntityProjectionExpression(targetEntityType, leftJoinTable, true),
                                                                     true);
                            entityProjectionExpression.AddNavigationBinding(navigation, innerShaper);
                        }

                        return(innerShaper);
                    }
                }

                return(null);
            }