protected virtual void FlattenSubQuery(SubQueryExpression subQueryExpression, IFromClause fromClause, QueryModel queryModel, int destinationIndex) { ArgumentUtility.CheckNotNull("subQueryExpression", subQueryExpression); ArgumentUtility.CheckNotNull("fromClause", fromClause); ArgumentUtility.CheckNotNull("queryModel", queryModel); CheckFlattenable(subQueryExpression.QueryModel); var innerMainFromClause = subQueryExpression.QueryModel.MainFromClause; fromClause.CopyFromSource(innerMainFromClause); var innerSelectorMapping = new QuerySourceMapping(); innerSelectorMapping.AddMapping(fromClause, subQueryExpression.QueryModel.SelectClause.Selector); queryModel.TransformExpressions(ex => ReferenceReplacingExpressionVisitor.ReplaceClauseReferences(ex, innerSelectorMapping, false)); InsertBodyClauses(subQueryExpression.QueryModel.BodyClauses, queryModel, destinationIndex); var innerBodyClauseMapping = new QuerySourceMapping(); innerBodyClauseMapping.AddMapping(innerMainFromClause, new QuerySourceReferenceExpression(fromClause)); queryModel.TransformExpressions(ex => ReferenceReplacingExpressionVisitor.ReplaceClauseReferences(ex, innerBodyClauseMapping, false)); }
/// <summary> /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> protected override void FlattenSubQuery( [NotNull] SubQueryExpression subQueryExpression, [NotNull] IFromClause fromClause, [NotNull] QueryModel queryModel, int destinationIndex) { var subQueryModel = subQueryExpression.QueryModel; VisitQueryModel(subQueryModel); if (subQueryModel.ResultOperators .All(ro => ro is CastResultOperator) && !subQueryModel.BodyClauses.Any(bc => bc is OrderByClause) || queryModel.IsIdentityQuery() && !queryModel.ResultOperators.Any()) { string itemName; var innerMainFromClause = subQueryExpression.QueryModel.MainFromClause; var isGeneratedNameOuter = fromClause.HasGeneratedItemName(); if (innerMainFromClause.HasGeneratedItemName() && !isGeneratedNameOuter) { itemName = fromClause.ItemName; } else { itemName = innerMainFromClause.ItemName; } var fromClauseData = new FromClauseData( itemName, innerMainFromClause.ItemType, innerMainFromClause.FromExpression); fromClause.CopyFromSource(fromClauseData); UpdateQuerySourceMapping( queryModel, fromClause, subQueryExpression.QueryModel.SelectClause.Selector); InsertBodyClauses(subQueryExpression.QueryModel.BodyClauses, queryModel, destinationIndex); foreach (var resultOperator in subQueryModel.ResultOperators.Reverse()) { queryModel.ResultOperators.Insert(0, resultOperator); } UpdateQuerySourceMapping( queryModel, innerMainFromClause, new QuerySourceReferenceExpression(fromClause)); } }
/// <summary> /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> protected override void FlattenSubQuery( SubQueryExpression subQueryExpression, IFromClause fromClause, QueryModel queryModel, int destinationIndex) { var subQueryModel = subQueryExpression.QueryModel; VisitQueryModel(subQueryModel); // no groupby and no distinct var emptyQueryModelWithFlattenableResultOperatorInSubquery = !queryModel.BodyClauses.Any() && subQueryModel.ResultOperators.All( ro => ro is CastResultOperator || ro is DefaultIfEmptyResultOperator || ro is ExceptResultOperator || ro is OfTypeResultOperator || ro is ReverseResultOperator || ro is SkipResultOperator || ro is TakeResultOperator); // we can lift distinct however if the outer query has result operator that doesn't care about having correct element count var emptyQueryModelWithResultOperatorThatIgnoresElementCountAndDistinctInSubquery = !queryModel.BodyClauses.Any() && subQueryModel.ResultOperators.Any(ro => ro is DistinctResultOperator) && queryModel.ResultOperators.Any( ro => ro is ContainsResultOperator || ro is AnyResultOperator || ro is AllResultOperator || ro is MinResultOperator || ro is MaxResultOperator); var subqueryInMainClauseWithoutResultOperatorsProjectingItsMainClause = fromClause is MainFromClause && !subQueryModel.ResultOperators.Any() && subQueryModel.SelectClause.Selector is QuerySourceReferenceExpression subquerySelectorsQsre && subquerySelectorsQsre.ReferencedQuerySource == subQueryModel.MainFromClause; if (subQueryModel.ResultOperators.All(ro => ro is CastResultOperator) && !subQueryModel.BodyClauses.Any(bc => bc is OrderByClause) && subQueryModel.SelectClause.Selector.NodeType != ExpressionType.MemberInit && subQueryModel.SelectClause.Selector.NodeType != ExpressionType.New || queryModel.IsIdentityQuery() && !queryModel.ResultOperators.Any() || emptyQueryModelWithFlattenableResultOperatorInSubquery || emptyQueryModelWithResultOperatorThatIgnoresElementCountAndDistinctInSubquery || subqueryInMainClauseWithoutResultOperatorsProjectingItsMainClause) { string itemName; var querySourceMapping = new QuerySourceMapping(); var clonedSubQueryModel = subQueryModel.Clone(querySourceMapping); UpdateQueryAnnotations(subQueryModel, querySourceMapping); var innerMainFromClause = clonedSubQueryModel.MainFromClause; var isGeneratedNameOuter = fromClause.HasGeneratedItemName(); if (innerMainFromClause.HasGeneratedItemName() && !isGeneratedNameOuter) { itemName = fromClause.ItemName; } else { itemName = innerMainFromClause.ItemName; } var fromClauseData = new FromClauseData( itemName, innerMainFromClause.ItemType, innerMainFromClause.FromExpression); fromClause.CopyFromSource(fromClauseData); var newExpression = clonedSubQueryModel.SelectClause.Selector; var newExpressionTypeInfo = newExpression.Type.GetTypeInfo(); var castResultOperatorTypes = clonedSubQueryModel.ResultOperators.OfType <CastResultOperator>().Select(cre => cre.CastItemType).ToList(); var type = castResultOperatorTypes.LastOrDefault(t => newExpressionTypeInfo.IsAssignableFrom(t.GetTypeInfo())); if (type != null && type != newExpression.Type) { newExpression = Expression.Convert(newExpression, type); } UpdateQuerySourceMapping( queryModel, fromClause, newExpression); InsertBodyClauses(clonedSubQueryModel.BodyClauses, queryModel, destinationIndex); foreach (var resultOperator in clonedSubQueryModel.ResultOperators.Where(ro => !(ro is CastResultOperator)).Reverse()) { queryModel.ResultOperators.Insert(0, resultOperator); } UpdateQuerySourceMapping( queryModel, innerMainFromClause, new QuerySourceReferenceExpression(fromClause)); } }
/// <summary> /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> protected override void FlattenSubQuery( [NotNull] SubQueryExpression subQueryExpression, [NotNull] IFromClause fromClause, [NotNull] QueryModel queryModel, int destinationIndex) { var subQueryModel = subQueryExpression.QueryModel; VisitQueryModel(subQueryModel); if ((subQueryModel.ResultOperators .All(ro => ro is CastResultOperator) && !subQueryModel.BodyClauses.Any(bc => bc is OrderByClause)) || (queryModel.IsIdentityQuery() && !queryModel.ResultOperators.Any())) { string itemName; var innerMainFromClause = subQueryExpression.QueryModel.MainFromClause; var isGeneratedNameOuter = fromClause.HasGeneratedItemName(); if (innerMainFromClause.HasGeneratedItemName() && !isGeneratedNameOuter) { itemName = fromClause.ItemName; } else { itemName = innerMainFromClause.ItemName; } var fromClauseData = new FromClauseData( itemName, innerMainFromClause.ItemType, innerMainFromClause.FromExpression); fromClause.CopyFromSource(fromClauseData); var innerSelectorMapping = new QuerySourceMapping(); innerSelectorMapping.AddMapping(fromClause, subQueryExpression.QueryModel.SelectClause.Selector); queryModel.TransformExpressions( ex => ReferenceReplacingExpressionVisitor .ReplaceClauseReferences(ex, innerSelectorMapping, false)); InsertBodyClauses(subQueryExpression.QueryModel.BodyClauses, queryModel, destinationIndex); foreach (var resultOperator in subQueryModel.ResultOperators.Reverse()) { queryModel.ResultOperators.Insert(0, resultOperator); } var innerBodyClauseMapping = new QuerySourceMapping(); innerBodyClauseMapping .AddMapping(innerMainFromClause, new QuerySourceReferenceExpression(fromClause)); queryModel.TransformExpressions( ex => ReferenceReplacingExpressionVisitor .ReplaceClauseReferences(ex, innerBodyClauseMapping, false)); foreach (var queryAnnotation in _queryAnnotations .Where(qa => qa.QuerySource == subQueryExpression.QueryModel.MainFromClause)) { queryAnnotation.QuerySource = fromClause; queryAnnotation.QueryModel = queryModel; } } }