Skip to content
Merged
8 changes: 8 additions & 0 deletions src/EFCore.Relational/EFCore.Relational.baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -14397,6 +14397,14 @@
}
]
},
{
"Type": "class Microsoft.EntityFrameworkCore.Query.RelationalNavigationExpansionExtensibilityHelper : Microsoft.EntityFrameworkCore.Query.NavigationExpansionExtensibilityHelper, Microsoft.EntityFrameworkCore.Query.INavigationExpansionExtensibilityHelper",
"Methods": [
{
"Member": "RelationalNavigationExpansionExtensibilityHelper(Microsoft.EntityFrameworkCore.Query.NavigationExpansionExtensibilityHelperDependencies dependencies);"
}
]
},
{
"Type": "class Microsoft.EntityFrameworkCore.Metadata.Conventions.RelationalNavigationJsonPropertyNameAttributeConvention : Microsoft.EntityFrameworkCore.Metadata.Conventions.NavigationAttributeConventionBase<System.Text.Json.Serialization.JsonPropertyNameAttribute>, Microsoft.EntityFrameworkCore.Metadata.Conventions.INavigationAddedConvention, Microsoft.EntityFrameworkCore.Metadata.Conventions.IConvention",
"Methods": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd<IRelationalSqlTranslatingExpressionVisitorFactory, RelationalSqlTranslatingExpressionVisitorFactory>();
TryAdd<ISqlExpressionFactory, SqlExpressionFactory>();
TryAdd<IQueryTranslationPreprocessorFactory, RelationalQueryTranslationPreprocessorFactory>();
TryAdd<INavigationExpansionExtensibilityHelper, RelationalNavigationExpansionExtensibilityHelper>();
TryAdd<IRelationalParameterBasedSqlProcessorFactory, RelationalParameterBasedSqlProcessorFactory>();
TryAdd<IRelationalQueryStringFactory, RelationalQueryStringFactory>();
TryAdd<IQueryCompilationContextFactory, RelationalQueryCompilationContextFactory>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Query;

/// <summary>
/// Relational-specific service which helps with various aspects of navigation expansion extensibility.
/// </summary>
/// <remarks>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Singleton" />. This means a single instance
/// is used by many <see cref="DbContext" /> instances. The implementation must be thread-safe.
/// This service cannot depend on services registered as <see cref="ServiceLifetime.Scoped" />.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-providers">Implementation of database providers and extensions</see>
/// and <see href="https://aka.ms/efcore-docs-how-query-works">How EF Core queries work</see> for more information and examples.
/// </para>
/// </remarks>
public class RelationalNavigationExpansionExtensibilityHelper : NavigationExpansionExtensibilityHelper, INavigationExpansionExtensibilityHelper
{
/// <summary>
/// Creates a new instance of the <see cref="RelationalNavigationExpansionExtensibilityHelper" /> class.
/// </summary>
/// <param name="dependencies">The dependencies to use.</param>
public RelationalNavigationExpansionExtensibilityHelper(NavigationExpansionExtensibilityHelperDependencies dependencies)
: base(dependencies)
{
}

/// <inheritdoc />
bool INavigationExpansionExtensibilityHelper.SupportsNavigationExpansionJoins
=> true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
/// 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 class SqlServerNavigationExpansionExtensibilityHelper : NavigationExpansionExtensibilityHelper
public class SqlServerNavigationExpansionExtensibilityHelper : RelationalNavigationExpansionExtensibilityHelper
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
5 changes: 5 additions & 0 deletions src/EFCore/EFCore.baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -14697,6 +14697,11 @@
{
"Member": "void ValidateQueryRootCreation(Microsoft.EntityFrameworkCore.Metadata.IEntityType entityType, Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression? source);"
}
],
"Properties": [
{
"Member": "bool SupportsNavigationExpansionJoins { get; }"
}
]
},
{
Expand Down
9 changes: 9 additions & 0 deletions src/EFCore/Query/INavigationExpansionExtensibilityHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,13 @@ public interface INavigationExpansionExtensibilityHelper
/// <param name="first">The first query root.</param>
/// <param name="second">The second query root.</param>
bool AreQueryRootsCompatible(EntityQueryRootExpression? first, EntityQueryRootExpression? second);

/// <summary>
/// Whether navigation expansion may add joins to a source to translate aggregates over
/// reference navigations inside a GroupBy result selector into the grouped query, instead
/// of a correlated subquery per group. Providers that do not support joins must return
/// <see langword="false" />.
/// </summary>
bool SupportsNavigationExpansionJoins
=> false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,15 @@ public GroupByNavigationExpansionExpression(
ParameterExpression groupingParameter,
NavigationTreeNode currentTree,
Expression pendingSelector,
string innerParameterName)
string innerParameterName,
NavigationExpansionExpression? parent = null,
LambdaExpression? originalKeySelector = null)
{
Source = source;
CurrentParameter = groupingParameter;
Type = source.Type;
Parent = parent;
OriginalKeySelector = originalKeySelector;
GroupingEnumerable = new NavigationExpansionExpression(
Call(QueryableMethods.AsQueryable.MakeGenericMethod(CurrentParameter.Type.GetGenericArguments()[1]), CurrentParameter),
currentTree,
Expand All @@ -320,11 +324,28 @@ public GroupByNavigationExpansionExpression(

public NavigationExpansionExpression GroupingEnumerable { get; }

/// <summary>
/// The pre-GroupBy source, stashed so the aggregate lift can widen it with joins and rebuild
/// the GroupBy. Cleared as soon as an operator is composed over the grouping sequence.
/// </summary>
public NavigationExpansionExpression? Parent { get; private set; }

/// <summary>
/// The unprocessed key selector, re-derivable over the widened parent because navigation
/// expansion is idempotent (re-expansion reuses existing joins).
/// </summary>
public LambdaExpression? OriginalKeySelector { get; }

public Type SourceElementType
=> CurrentParameter.Type;

public void UpdateSource(Expression source)
=> Source = source;
{
Source = source;

// Operators over the grouping sequence cannot be carried through the aggregate lift.
Parent = null;
}

public override ExpressionType NodeType
=> ExpressionType.Extension;
Expand Down
Loading
Loading