diff --git a/EFCore.sln.DotSettings b/EFCore.sln.DotSettings
index 6e44bcd4b12..70456651788 100644
--- a/EFCore.sln.DotSettings
+++ b/EFCore.sln.DotSettings
@@ -301,6 +301,7 @@ The .NET Foundation licenses this file to you under the MIT license.
TrueTrueTrue
+ TrueTrueTrueTrue
diff --git a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
index 638ce909358..d027c9f2674 100644
--- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
+++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
@@ -62,6 +62,7 @@ public static readonly IDictionary RelationalServi
{ typeof(IRawSqlCommandBuilder), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IQuerySqlGeneratorFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IModificationCommandFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
+ { typeof(IRelationalLiftableConstantFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ICommandBatchPreparer), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IModificationCommandBatchFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IRelationalSqlTranslatingExpressionVisitorFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
@@ -185,6 +186,9 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd();
TryAdd();
TryAdd();
+ TryAdd(p => p.GetRequiredService());
+ TryAdd();
+ TryAdd();
ServiceCollectionMap.GetInfrastructure()
.AddDependencySingleton()
diff --git a/src/EFCore.Relational/Query/IRelationalLiftableConstantFactory.cs b/src/EFCore.Relational/Query/IRelationalLiftableConstantFactory.cs
new file mode 100644
index 00000000000..7ba1f1112eb
--- /dev/null
+++ b/src/EFCore.Relational/Query/IRelationalLiftableConstantFactory.cs
@@ -0,0 +1,12 @@
+// 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;
+
+public interface IRelationalLiftableConstantFactory : ILiftableConstantFactory
+{
+ LiftableConstantExpression CreateLiftableConstant(
+ Expression> resolverExpression,
+ string variableName,
+ Type type);
+}
diff --git a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs
index 2e26170ec3e..fcf5af8d4ff 100644
--- a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs
@@ -653,7 +653,8 @@ private ProjectionBindingExpression AddClientProjection(Expression expression, T
return new ProjectionBindingExpression(_selectExpression, existingIndex, type);
}
- private static T GetParameterValue(QueryContext queryContext, string parameterName)
+ // Public because can get referenced by precompiled shaper code
+ public static T GetParameterValue(QueryContext queryContext, string parameterName)
#pragma warning restore IDE0052 // Remove unread private members
=> (T)queryContext.ParameterValues[parameterName]!;
diff --git a/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs b/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
index 53a3c372294..6df78f98407 100644
--- a/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
+++ b/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
@@ -6,6 +6,28 @@
namespace Microsoft.EntityFrameworkCore.Query.Internal;
+public static class SingleQueryingEnumerable
+{
+ public static SingleQueryingEnumerable Create(
+ RelationalQueryContext relationalQueryContext,
+ RelationalCommandCache relationalCommandCache,
+ IReadOnlyList? readerColumns,
+ Func shaper,
+ Type contextType,
+ bool standAloneStateManager,
+ bool detailedErrorsEnabled,
+ bool threadSafetyChecksEnabled)
+ => new(
+ relationalQueryContext,
+ relationalCommandCache,
+ readerColumns,
+ shaper,
+ contextType,
+ standAloneStateManager,
+ detailedErrorsEnabled,
+ threadSafetyChecksEnabled);
+}
+
///
/// 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
diff --git a/src/EFCore.Relational/Query/RelationalLiftableConstantFactory.cs b/src/EFCore.Relational/Query/RelationalLiftableConstantFactory.cs
new file mode 100644
index 00000000000..016f55ce9f5
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalLiftableConstantFactory.cs
@@ -0,0 +1,13 @@
+// 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;
+
+public class RelationalLiftableConstantFactory : LiftableConstantFactory, IRelationalLiftableConstantFactory
+{
+ public virtual LiftableConstantExpression CreateLiftableConstant(
+ Expression> resolverExpression,
+ string variableName,
+ Type type)
+ => new(resolverExpression, variableName, type);
+}
diff --git a/src/EFCore.Relational/Query/RelationalLiftableConstantProcessor.cs b/src/EFCore.Relational/Query/RelationalLiftableConstantProcessor.cs
new file mode 100644
index 00000000000..06cb5c6db4c
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalLiftableConstantProcessor.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Query.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+#pragma warning disable EF1001 // LiftableConstantProcessor is internal
+
+public class RelationalLiftableConstantProcessor : LiftableConstantProcessor
+{
+ private RelationalMaterializerLiftableConstantContext _relationalMaterializerLiftableConstantContext;
+
+ public RelationalLiftableConstantProcessor(
+ ShapedQueryCompilingExpressionVisitorDependencies dependencies,
+ RelationalShapedQueryCompilingExpressionVisitorDependencies relationalDependencies)
+ : base(dependencies)
+ => _relationalMaterializerLiftableConstantContext = new(dependencies, relationalDependencies);
+
+ protected override ConstantExpression InlineConstant(LiftableConstantExpression liftableConstant)
+ {
+ if (liftableConstant.ResolverExpression is Expression>
+ resolverExpression)
+ {
+ var resolver = resolverExpression.Compile(preferInterpretation: true);
+ var value = resolver(_relationalMaterializerLiftableConstantContext);
+ return Expression.Constant(value, liftableConstant.Type);
+ }
+
+ return base.InlineConstant(liftableConstant);
+ }
+}
diff --git a/src/EFCore.Relational/Query/RelationalMaterializerLiftableConstantContext.cs b/src/EFCore.Relational/Query/RelationalMaterializerLiftableConstantContext.cs
new file mode 100644
index 00000000000..0326f8e323e
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalMaterializerLiftableConstantContext.cs
@@ -0,0 +1,9 @@
+// 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;
+
+public record RelationalMaterializerLiftableConstantContext(
+ ShapedQueryCompilingExpressionVisitorDependencies Dependencies,
+ RelationalShapedQueryCompilingExpressionVisitorDependencies RelationalDependencies)
+ : MaterializerLiftableConstantContext(Dependencies);
diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs
index 33b01fd885a..08536bd1156 100644
--- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs
+++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs
@@ -11,7 +11,14 @@ namespace Microsoft.EntityFrameworkCore.Query;
public partial class RelationalShapedQueryCompilingExpressionVisitor
{
- private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisitor
+ ///
+ /// 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.
+ ///
+ [EntityFrameworkInternal]
+ public sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisitor
{
private static readonly MethodInfo ThrowReadValueExceptionMethod =
typeof(ShaperProcessingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(ThrowReadValueException))!;
@@ -160,7 +167,14 @@ private static void IncludeReference
}
}
- private static void InitializeIncludeCollection(
+ ///
+ /// 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.
+ ///
+ [EntityFrameworkInternal]
+ public static void InitializeIncludeCollection(
int collectionId,
QueryContext queryContext,
DbDataReader dbDataReader,
@@ -201,7 +215,14 @@ private static void InitializeIncludeCollection(
resultCoordinator.SetSingleQueryCollectionContext(collectionId, collectionMaterializationContext);
}
- private static void PopulateIncludeCollection(
+ ///
+ /// 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.
+ ///
+ [EntityFrameworkInternal]
+ public static void PopulateIncludeCollection(
int collectionId,
QueryContext queryContext,
DbDataReader dbDataReader,
@@ -209,9 +230,12 @@ private static void PopulateIncludeCollection
Func parentIdentifier,
Func outerIdentifier,
Func selfIdentifier,
- IReadOnlyList parentIdentifierValueComparers,
- IReadOnlyList outerIdentifierValueComparers,
- IReadOnlyList selfIdentifierValueComparers,
+ IReadOnlyList> parentIdentifierValueComparers,
+ IReadOnlyList> outerIdentifierValueComparers,
+ IReadOnlyList> selfIdentifierValueComparers,
+ // IReadOnlyList parentIdentifierValueComparers,
+ // IReadOnlyList outerIdentifierValueComparers,
+ // IReadOnlyList selfIdentifierValueComparers,
Func innerShaper,
INavigationBase? inverseNavigation,
Action fixup,
@@ -229,14 +253,14 @@ private static void PopulateIncludeCollection
return;
}
- if (!CompareIdentifiers(
+ if (!CompareIdentifiers2(
outerIdentifierValueComparers,
outerIdentifier(queryContext, dbDataReader), collectionMaterializationContext.OuterIdentifier))
{
// Outer changed so collection has ended. Materialize last element.
GenerateCurrentElementIfPending();
// If parent also changed then this row is now pointing to element of next collection
- if (!CompareIdentifiers(
+ if (!CompareIdentifiers2(
parentIdentifierValueComparers,
parentIdentifier(queryContext, dbDataReader), collectionMaterializationContext.ParentIdentifier))
{
@@ -255,7 +279,7 @@ private static void PopulateIncludeCollection
if (collectionMaterializationContext.SelfIdentifier != null)
{
- if (CompareIdentifiers(selfIdentifierValueComparers, innerKey, collectionMaterializationContext.SelfIdentifier))
+ if (CompareIdentifiers2(selfIdentifierValueComparers, innerKey, collectionMaterializationContext.SelfIdentifier))
{
// repeated row for current element
// If it is pending materialization then it may have nested elements
@@ -319,7 +343,14 @@ void GenerateCurrentElementIfPending()
}
}
- private static void InitializeSplitIncludeCollection(
+ ///
+ /// 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.
+ ///
+ [EntityFrameworkInternal]
+ public static void InitializeSplitIncludeCollection(
int collectionId,
QueryContext queryContext,
DbDataReader parentDataReader,
@@ -358,7 +389,14 @@ private static void InitializeSplitIncludeCollection
resultCoordinator.SetSplitQueryCollectionContext(collectionId, splitQueryCollectionContext);
}
- private static void PopulateSplitIncludeCollection(
+ ///
+ /// 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.
+ ///
+ [EntityFrameworkInternal]
+ public static void PopulateSplitIncludeCollection(
int collectionId,
RelationalQueryContext queryContext,
IExecutionStrategy executionStrategy,
@@ -367,7 +405,8 @@ private static void PopulateSplitIncludeCollection childIdentifier,
- IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
+ // IReadOnlyList identifierValueComparers,
Func innerShaper,
Action? relatedDataLoaders,
INavigationBase? inverseNavigation,
@@ -414,7 +453,7 @@ static RelationalDataReader InitializeReader(
{
while (dataReaderContext.HasNext ?? dbDataReader.Read())
{
- if (!CompareIdentifiers(
+ if (!CompareIdentifiers2(
identifierValueComparers,
splitQueryCollectionContext.ParentIdentifier, childIdentifier(queryContext, dbDataReader)))
{
@@ -451,7 +490,8 @@ private static async Task PopulateSplitIncludeCollectionAsync childIdentifier,
- IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
+ // IReadOnlyList identifierValueComparers,
Func innerShaper,
Func? relatedDataLoaders,
INavigationBase? inverseNavigation,
@@ -506,7 +546,7 @@ static async Task InitializeReaderAsync(
{
while (dataReaderContext.HasNext ?? await dbDataReader.ReadAsync(queryContext.CancellationToken).ConfigureAwait(false))
{
- if (!CompareIdentifiers(
+ if (!CompareIdentifiers2(
identifierValueComparers,
splitQueryCollectionContext.ParentIdentifier, childIdentifier(queryContext, dbDataReader)))
{
@@ -538,7 +578,8 @@ static async Task InitializeReaderAsync(
}
}
- private static TCollection InitializeCollection(
+ [EntityFrameworkInternal]
+ public static TCollection InitializeCollection(
int collectionId,
QueryContext queryContext,
DbDataReader dbDataReader,
@@ -560,7 +601,8 @@ private static TCollection InitializeCollection(
return (TCollection)collection;
}
- private static void PopulateCollection(
+ [EntityFrameworkInternal]
+ public static void PopulateCollection(
int collectionId,
QueryContext queryContext,
DbDataReader dbDataReader,
@@ -1066,6 +1108,20 @@ private static async Task TaskAwaiter(Func[] taskFactories)
}
}
+ private static bool CompareIdentifiers2(IReadOnlyList> valueComparers, object[] left, object[] right)
+ {
+ // Ignoring size check on all for perf as they should be same unless bug in code.
+ for (var i = 0; i < left.Length; i++)
+ {
+ if (!valueComparers[i](left[i], right[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private static bool CompareIdentifiers(IReadOnlyList valueComparers, object[] left, object[] right)
{
// Ignoring size check on all for perf as they should be same unless bug in code.
diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs
index 3a64627b326..64ff08e8a60 100644
--- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs
@@ -14,7 +14,13 @@ namespace Microsoft.EntityFrameworkCore.Query;
public partial class RelationalShapedQueryCompilingExpressionVisitor
{
- private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisitor
+ ///
+ /// 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.
+ ///
+ public sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisitor
{
///
/// Reading database values
@@ -77,6 +83,9 @@ private static readonly MethodInfo Utf8JsonReaderGetStringMethod
private static readonly MethodInfo EnumParseMethodInfo
= typeof(Enum).GetMethod(nameof(Enum.Parse), new[] { typeof(Type), typeof(string) })!;
+ private static readonly MethodInfo ReadColumnCreateMethod
+ = typeof(ReaderColumn).GetMethod(nameof(ReaderColumn.Create))!;
+
private readonly RelationalShapedQueryCompilingExpressionVisitor _parentVisitor;
private readonly ISet? _tags;
private readonly bool _isTracking;
@@ -249,8 +258,9 @@ private ShaperProcessingExpressionVisitor(
public LambdaExpression ProcessRelationalGroupingResult(
RelationalGroupByResultExpression relationalGroupByResultExpression,
- out RelationalCommandCache relationalCommandCache,
- out IReadOnlyList? readerColumns,
+ out Expression relationalCommandCache,
+ out Func readerColumns,
+ // out IReadOnlyList? readerColumns,
out LambdaExpression keySelector,
out LambdaExpression keyIdentifier,
out LambdaExpression? relatedDataLoaders,
@@ -279,8 +289,9 @@ public LambdaExpression ProcessRelationalGroupingResult(
public LambdaExpression ProcessShaper(
Expression shaperExpression,
- out RelationalCommandCache? relationalCommandCache,
- out IReadOnlyList? readerColumns,
+ out Expression relationalCommandCache,
+ out Func readerColumns,
+ // out IReadOnlyList? readerColumns,
out LambdaExpression? relatedDataLoaders,
ref int collectionId)
{
@@ -293,13 +304,8 @@ public LambdaExpression ProcessShaper(
_expressions.Add(result);
result = Block(_variables, _expressions);
- relationalCommandCache = new RelationalCommandCache(
- _parentVisitor.Dependencies.MemoryCache,
- _parentVisitor.RelationalDependencies.QuerySqlGeneratorFactory,
- _parentVisitor.RelationalDependencies.RelationalParameterBasedSqlProcessorFactory,
- _selectExpression,
- _parentVisitor._useRelationalNulls);
- readerColumns = _readerColumns;
+ relationalCommandCache = _parentVisitor.CreateRelationalCommandCacheExpression(_selectExpression);
+ readerColumns = CreateReaderColumnsExpression();
return Lambda(
result,
@@ -320,14 +326,9 @@ public LambdaExpression ProcessShaper(
result = Block(_variables, _expressions);
relationalCommandCache = _generateCommandCache
- ? new RelationalCommandCache(
- _parentVisitor.Dependencies.MemoryCache,
- _parentVisitor.RelationalDependencies.QuerySqlGeneratorFactory,
- _parentVisitor.RelationalDependencies.RelationalParameterBasedSqlProcessorFactory,
- _selectExpression,
- _parentVisitor._useRelationalNulls)
- : null;
- readerColumns = _readerColumns;
+ ? _parentVisitor.CreateRelationalCommandCacheExpression(_selectExpression)
+ : Expression.Constant(null, typeof(RelationalCommandCache));
+ readerColumns = CreateReaderColumnsExpression();
return Lambda(
result,
@@ -408,14 +409,9 @@ public LambdaExpression ProcessShaper(
}
relationalCommandCache = _generateCommandCache
- ? new RelationalCommandCache(
- _parentVisitor.Dependencies.MemoryCache,
- _parentVisitor.RelationalDependencies.QuerySqlGeneratorFactory,
- _parentVisitor.RelationalDependencies.RelationalParameterBasedSqlProcessorFactory,
- _selectExpression,
- _parentVisitor._useRelationalNulls)
- : null;
- readerColumns = _readerColumns;
+ ? _parentVisitor.CreateRelationalCommandCacheExpression(_selectExpression)
+ : Expression.Constant(null, typeof(RelationalCommandCache));;
+ readerColumns = CreateReaderColumnsExpression();
collectionId = _collectionId;
@@ -452,8 +448,15 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
? value
: propertyMap.Values.Max() + 1;
- var updatedExpression = newExpression.Update(
- new[] { Constant(ValueBuffer.Empty), newExpression.Arguments[1] });
+ var updatedExpression = newExpression.Update(
+ new[]
+ {
+ _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
+ _ => ValueBuffer.Empty,
+ "emptyValueBuffer",
+ typeof(ValueBuffer)),
+ newExpression.Arguments[1]
+ });
return Assign(binaryExpression.Left, updatedExpression);
}
@@ -597,7 +600,7 @@ protected override Expression VisitExtension(Expression extensionExpression)
}
else
{
- var entityParameter = Parameter(shaper.Type);
+ var entityParameter = Parameter(shaper.Type, "entity");
_variables.Add(entityParameter);
if (shaper.StructuralType is IEntityType entityType
&& entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy)
@@ -718,7 +721,7 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
var projection = _selectExpression.Projection[projectionIndex];
var nullable = IsNullableProjection(projection);
- var valueParameter = Parameter(projectionBindingExpression.Type);
+ var valueParameter = Parameter(projectionBindingExpression.Type, "value" + (_variables.Count + 1));
_variables.Add(valueParameter);
_expressions.Add(
@@ -769,13 +772,13 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
_readerColumns)
.ProcessShaper(relationalCollectionShaperExpression.InnerShaper, out _, out _, out _, ref _collectionId);
- var entityType = entity.Type;
+ var entityClrType = entity.Type;
var navigation = includeExpression.Navigation;
- var includingEntityType = navigation.DeclaringEntityType.ClrType;
- if (includingEntityType != entityType
- && includingEntityType.IsAssignableFrom(entityType))
+ var includingEntityClrType = navigation.DeclaringEntityType.ClrType;
+ if (includingEntityClrType != entityClrType
+ && includingEntityClrType.IsAssignableFrom(entityClrType))
{
- includingEntityType = entityType;
+ includingEntityClrType = entityClrType;
}
_inline = true;
@@ -799,51 +802,68 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
_includeExpressions.Add(
Call(
- InitializeIncludeCollectionMethodInfo.MakeGenericMethod(entityType, includingEntityType),
+ InitializeIncludeCollectionMethodInfo.MakeGenericMethod(entityClrType, includingEntityClrType),
collectionIdConstant,
QueryCompilationContext.QueryContextParameter,
_dataReaderParameter,
_resultCoordinatorParameter,
entity,
- Constant(parentIdentifierLambda.Compile()),
- Constant(outerIdentifierLambda.Compile()),
- Constant(navigation),
- Constant(
- navigation.IsShadowProperty()
- ? null
- : navigation.GetCollectionAccessor(), typeof(IClrCollectionAccessor)),
+ parentIdentifierLambda,
+ outerIdentifierLambda,
+ // Constant(navigation),
+ _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
+ c => c.Dependencies.Model.FindEntityType(navigation.DeclaringEntityType.Name)!.FindNavigation(navigation.Name)!,
+ navigation.Name + "Navigation",
+ typeof(INavigationBase)),
+ // Constant(navigation.IsShadowProperty()
+ // ? null
+ // : navigation.GetCollectionAccessor(), typeof(IClrCollectionAccessor)),
+ navigation.IsShadowProperty()
+ ? Constant(null, typeof(IClrCollectionAccessor))
+ : _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
+ c => c.Dependencies.Model.FindEntityType(navigation.DeclaringEntityType.Name)!.FindNavigation(navigation.Name)!
+ .GetCollectionAccessor()!,
+ navigation.Name + "NavigationCollectionAccessor",
+ typeof(IClrCollectionAccessor)),
Constant(_isTracking),
#pragma warning disable EF1001 // Internal EF Core API usage.
Constant(includeExpression.SetLoaded)));
#pragma warning restore EF1001 // Internal EF Core API usage.
- var relatedEntityType = innerShaper.ReturnType;
+ var relatedEntityClrType = innerShaper.ReturnType;
var inverseNavigation = navigation.Inverse;
_collectionPopulatingExpressions!.Add(
Call(
- PopulateIncludeCollectionMethodInfo.MakeGenericMethod(includingEntityType, relatedEntityType),
+ PopulateIncludeCollectionMethodInfo.MakeGenericMethod(includingEntityClrType, relatedEntityClrType),
collectionIdConstant,
QueryCompilationContext.QueryContextParameter,
_dataReaderParameter,
_resultCoordinatorParameter,
- Constant(parentIdentifierLambda.Compile()),
- Constant(outerIdentifierLambda.Compile()),
- Constant(selfIdentifierLambda.Compile()),
- Constant(
- relationalCollectionShaperExpression.ParentIdentifierValueComparers,
- typeof(IReadOnlyList)),
- Constant(
- relationalCollectionShaperExpression.OuterIdentifierValueComparers,
- typeof(IReadOnlyList)),
- Constant(
- relationalCollectionShaperExpression.SelfIdentifierValueComparers,
- typeof(IReadOnlyList)),
- Constant(innerShaper.Compile()),
- Constant(inverseNavigation, typeof(INavigationBase)),
- Constant(
- GenerateFixup(
- includingEntityType, relatedEntityType, navigation, inverseNavigation).Compile()),
+ parentIdentifierLambda,
+ outerIdentifierLambda,
+ selfIdentifierLambda,
+ NewArrayInit(typeof(Func