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)); }
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; }
/// <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; }
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)); }
/// <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)); } }
/// <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)); }
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); }
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); }