Exemplo n.º 1
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 VisitExtension(Expression extensionExpression)
        {
            Check.NotNull(extensionExpression, nameof(extensionExpression));

            switch (extensionExpression)
            {
            case EntityShaperExpression entityShaperExpression:
            {
                var projectionBindingExpression = (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression;
                VerifySelectExpression(projectionBindingExpression);

                if (_clientEval)
                {
                    var entityProjection = (EntityProjectionExpression)_selectExpression.GetMappedProjection(
                        projectionBindingExpression.ProjectionMember);

                    return(entityShaperExpression.Update(
                               new ProjectionBindingExpression(
                                   _selectExpression, _selectExpression.AddToProjection(entityProjection), typeof(ValueBuffer))));
                }

                _projectionMapping[_projectionMembers.Peek()]
                    = _selectExpression.GetMappedProjection(projectionBindingExpression.ProjectionMember);

                return(entityShaperExpression.Update(
                           new ProjectionBindingExpression(_selectExpression, _projectionMembers.Peek(), typeof(ValueBuffer))));
            }

            case MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression:
                return(materializeCollectionNavigationExpression.Navigation is INavigation embeddableNavigation &&
                       embeddableNavigation.IsEmbedded()
                            ? base.Visit(materializeCollectionNavigationExpression.Subquery)
                            : base.VisitExtension(materializeCollectionNavigationExpression));

            case IncludeExpression includeExpression:
                if (!_clientEval)
                {
                    return(null);
                }

                if (!(includeExpression.Navigation is INavigation includableNavigation &&
                      includableNavigation.IsEmbedded()))
                {
                    throw new InvalidOperationException(
                              CosmosStrings.NonEmbeddedIncludeNotSupported(includeExpression.Navigation));
                }

                _includedNavigations.Push(includableNavigation);

                var newIncludeExpression = base.VisitExtension(includeExpression);

                _includedNavigations.Pop();

                return(newIncludeExpression);

            default:
                throw new InvalidOperationException(CoreStrings.TranslationFailed(extensionExpression.Print()));
            }
        }
            protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
            {
                Check.NotNull(methodCallExpression, nameof(methodCallExpression));

                var method        = methodCallExpression.Method;
                var genericMethod = method.IsGenericMethod ? method.GetGenericMethodDefinition() : null;

                if (genericMethod == EntityFrameworkCore.Infrastructure.ExpressionExtensions.ValueBufferTryReadValueMethod)
                {
                    var        property = methodCallExpression.Arguments[2].GetConstantValue <IProperty>();
                    Expression innerExpression;
                    if (methodCallExpression.Arguments[0] is ProjectionBindingExpression projectionBindingExpression)
                    {
                        var projection = GetProjection(projectionBindingExpression);

                        innerExpression = Expression.Convert(
                            CreateReadJTokenExpression(_jObjectParameter, projection.Alias),
                            typeof(JObject));
                    }
                    else
                    {
                        innerExpression = _materializationContextBindings[
                            (ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object];
                    }

                    return(CreateGetValueExpression(innerExpression, property, methodCallExpression.Type));
                }

                if (method.DeclaringType == typeof(Enumerable) &&
                    method.Name == nameof(Enumerable.Select) &&
                    genericMethod == EnumerableMethods.Select)
                {
                    var lambda = (LambdaExpression)methodCallExpression.Arguments[1];
                    if (lambda.Body is IncludeExpression includeExpression)
                    {
                        if (!(includeExpression.Navigation is INavigation navigation) ||
                            navigation.IsOnDependent ||
                            navigation.ForeignKey.DeclaringEntityType.IsDocumentRoot())
                        {
                            throw new InvalidOperationException(
                                      CosmosStrings.NonEmbeddedIncludeNotSupported(includeExpression.Navigation));
                        }

                        _pendingIncludes.Add(includeExpression);

                        Visit(includeExpression.EntityExpression);

                        // Includes on collections are processed when visiting CollectionShaperExpression
                        return(Visit(methodCallExpression.Arguments[0]));
                    }
                }

                return(base.VisitMethodCall(methodCallExpression));
            }
            protected override Expression VisitExtension(Expression extensionExpression)
            {
                Check.NotNull(extensionExpression, nameof(extensionExpression));

                switch (extensionExpression)
                {
                case ProjectionBindingExpression projectionBindingExpression:
                {
                    var projection = GetProjection(projectionBindingExpression);

                    return(CreateGetValueExpression(
                               _jObjectParameter,
                               projection.Alias,
                               projectionBindingExpression.Type, (projection.Expression as SqlExpression)?.TypeMapping));
                }

                case CollectionShaperExpression collectionShaperExpression:
                {
                    ObjectArrayProjectionExpression objectArrayProjection;
                    switch (collectionShaperExpression.Projection)
                    {
                    case ProjectionBindingExpression projectionBindingExpression:
                        var projection = GetProjection(projectionBindingExpression);
                        objectArrayProjection = (ObjectArrayProjectionExpression)projection.Expression;
                        break;

                    case ObjectArrayProjectionExpression objectArrayProjectionExpression:
                        objectArrayProjection = objectArrayProjectionExpression;
                        break;

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

                    var jArray           = _projectionBindings[objectArrayProjection];
                    var jObjectParameter = Expression.Parameter(typeof(JObject), jArray.Name + "Object");
                    var ordinalParameter = Expression.Parameter(typeof(int), jArray.Name + "Ordinal");

                    var accessExpression = objectArrayProjection.InnerProjection.AccessExpression;
                    _projectionBindings[accessExpression] = jObjectParameter;
                    _ownerMappings[accessExpression]      =
                        (objectArrayProjection.Navigation.DeclaringEntityType, objectArrayProjection.AccessExpression);
                    _ordinalParameterBindings[accessExpression] = Expression.Add(
                        ordinalParameter, Expression.Constant(1, typeof(int)));

                    var innerShaper = (BlockExpression)Visit(collectionShaperExpression.InnerShaper);

                    innerShaper = AddIncludes(innerShaper);

                    var entities = Expression.Call(
                        EnumerableMethods.SelectWithOrdinal.MakeGenericMethod(typeof(JObject), innerShaper.Type),
                        Expression.Call(
                            EnumerableMethods.Cast.MakeGenericMethod(typeof(JObject)),
                            jArray),
                        Expression.Lambda(innerShaper, jObjectParameter, ordinalParameter));

                    var navigation = collectionShaperExpression.Navigation;
                    return(Expression.Call(
                               _populateCollectionMethodInfo.MakeGenericMethod(navigation.TargetEntityType.ClrType, navigation.ClrType),
                               Expression.Constant(navigation.GetCollectionAccessor()),
                               entities));
                }

                case IncludeExpression includeExpression:
                {
                    if (!(includeExpression.Navigation is INavigation navigation) ||
                        navigation.IsOnDependent ||
                        navigation.ForeignKey.DeclaringEntityType.IsDocumentRoot())
                    {
                        throw new InvalidOperationException(
                                  CosmosStrings.NonEmbeddedIncludeNotSupported(includeExpression.Navigation));
                    }

                    var isFirstInclude = _pendingIncludes.Count == 0;
                    _pendingIncludes.Add(includeExpression);

                    var jObjectBlock = Visit(includeExpression.EntityExpression) as BlockExpression;

                    if (!isFirstInclude)
                    {
                        return(jObjectBlock);
                    }

                    Check.DebugAssert(jObjectBlock != null, "The first include must end up on a valid shaper block");

                    // These are the expressions added by JObjectInjectingExpressionVisitor
                    var jObjectCondition = (ConditionalExpression)jObjectBlock.Expressions[^ 1];