diff --git a/EFCore.sln.DotSettings b/EFCore.sln.DotSettings
index d1ddc281f56..6881961da98 100644
--- a/EFCore.sln.DotSettings
+++ b/EFCore.sln.DotSettings
@@ -309,6 +309,7 @@ The .NET Foundation licenses this file to you under the MIT license.
TrueTrueTrue
+ TrueTrueTrueTrue
diff --git a/src/EFCore.Analyzers/EFDiagnostics.cs b/src/EFCore.Analyzers/EFDiagnostics.cs
new file mode 100644
index 00000000000..49b461169f6
--- /dev/null
+++ b/src/EFCore.Analyzers/EFDiagnostics.cs
@@ -0,0 +1,14 @@
+// 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;
+
+///
+/// Contains IDs of diagnostics created by EF analyzers and other mechanisms.
+///
+public static class EFDiagnostics
+{
+ public const string InternalUsage = "EF1001";
+ public const string SuppressUninitializedDbSetRule = "EFSPR1001";
+ public const string PrecompiledQueryExperimental = "EF2001";
+}
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs
index 5d833b29426..791e04c0593 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.InMemory.Internal;
-using Microsoft.EntityFrameworkCore.Internal;
using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions;
namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal;
diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingInterceptor.cs
index ad8acaeceb4..ca081b841de 100644
--- a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingInterceptor.cs
+++ b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingInterceptor.cs
@@ -52,6 +52,7 @@ public virtual InstantiationBinding ModifyBinding(InstantiationBindingIntercepti
return new FactoryMethodBinding(
_proxyFactory,
+ Expression.Constant(_proxyFactory, typeof(IProxyFactory)),
CreateLazyLoadingProxyMethod,
new List
{
@@ -67,6 +68,7 @@ public virtual InstantiationBinding ModifyBinding(InstantiationBindingIntercepti
{
return new FactoryMethodBinding(
_proxyFactory,
+ Expression.Constant(_proxyFactory, typeof(IProxyFactory)),
CreateProxyMethod,
new List
{
diff --git a/src/EFCore.Relational/EFCore.Relational.csproj b/src/EFCore.Relational/EFCore.Relational.csproj
index 13eff3cb8d8..07d7d1ee954 100644
--- a/src/EFCore.Relational/EFCore.Relational.csproj
+++ b/src/EFCore.Relational/EFCore.Relational.csproj
@@ -8,8 +8,8 @@
Microsoft.EntityFrameworkCoretruetrue
- $(NoWarn);EF1003$(NoWarn);EF1004
+ $(NoWarn);EF2001
diff --git a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
index 93cd941c2f5..6d4ad282930 100644
--- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
+++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
@@ -63,6 +63,7 @@ public static readonly IDictionary RelationalServi
{ typeof(IQuerySqlGeneratorFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IModificationCommandFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ISqlAliasManagerFactory), 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) },
@@ -189,6 +190,9 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd();
TryAdd();
TryAdd();
+ TryAdd(p => p.GetRequiredService());
+ TryAdd();
+ TryAdd();
ServiceCollectionMap.GetInfrastructure()
.AddDependencySingleton()
@@ -204,6 +208,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
.AddDependencySingleton()
.AddDependencySingleton()
.AddDependencySingleton()
+ .AddDependencySingleton()
.AddDependencyScoped()
.AddDependencyScoped()
.AddDependencyScoped()
diff --git a/src/EFCore.Relational/Query/IRelationalLiftableConstantFactory.cs b/src/EFCore.Relational/Query/IRelationalLiftableConstantFactory.cs
new file mode 100644
index 00000000000..2c6da7cbebc
--- /dev/null
+++ b/src/EFCore.Relational/Query/IRelationalLiftableConstantFactory.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+///
+/// This is an experimental API used by the Entity Framework Core feature and it is 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.
+///
+[Experimental(EFDiagnostics.PrecompiledQueryExperimental)]
+public interface IRelationalLiftableConstantFactory : ILiftableConstantFactory
+{
+ ///
+ /// This is an experimental API used by the Entity Framework Core feature and it is 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.
+ ///
+ LiftableConstantExpression CreateLiftableConstant(
+ ConstantExpression originalExpression,
+ Expression> resolverExpression,
+ string variableName,
+ Type type);
+}
diff --git a/src/EFCore.Relational/Query/IRelationalQuotableExpression.cs b/src/EFCore.Relational/Query/IRelationalQuotableExpression.cs
index f8e59c412c8..40319ba777e 100644
--- a/src/EFCore.Relational/Query/IRelationalQuotableExpression.cs
+++ b/src/EFCore.Relational/Query/IRelationalQuotableExpression.cs
@@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Query;
/// Represents an expression that is quotable, that is, capable of returning an expression that, when evaluated, would construct an
/// expression identical to this one. Used to generate code for precompiled queries, which reconstructs this expression.
///
-[Experimental("EF1003")]
+[Experimental(EFDiagnostics.PrecompiledQueryExperimental)]
public interface IRelationalQuotableExpression
{
///
diff --git a/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs b/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs
index b53b4dc5d7c..73d4960150f 100644
--- a/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs
+++ b/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs
@@ -20,7 +20,7 @@ public class GroupBySingleQueryingEnumerable
private readonly IReadOnlyList? _readerColumns;
private readonly Func _keySelector;
private readonly Func _keyIdentifier;
- private readonly IReadOnlyList _keyIdentifierValueComparers;
+ private readonly IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Type _contextType;
private readonly IDiagnosticsLogger _queryLogger;
@@ -40,7 +40,7 @@ public GroupBySingleQueryingEnumerable(
IReadOnlyList? readerColumns,
Func keySelector,
Func keyIdentifier,
- IReadOnlyList keyIdentifierValueComparers,
+ IReadOnlyList> keyIdentifierValueComparers,
Func elementSelector,
Type contextType,
bool standAloneStateManager,
@@ -139,12 +139,12 @@ IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
}
- private static bool CompareIdentifiers(IReadOnlyList valueComparers, object[] left, object[] right)
+ 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.
for (var i = 0; i < left.Length; i++)
{
- if (!valueComparers[i].Equals(left[i], right[i]))
+ if (!valueComparers[i](left[i], right[i]))
{
return false;
}
@@ -160,7 +160,7 @@ private sealed class Enumerator : IEnumerator>
private readonly IReadOnlyList? _readerColumns;
private readonly Func _keySelector;
private readonly Func _keyIdentifier;
- private readonly IReadOnlyList _keyIdentifierValueComparers;
+ private readonly IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Type _contextType;
private readonly IDiagnosticsLogger _queryLogger;
@@ -344,7 +344,7 @@ private sealed class AsyncEnumerator : IAsyncEnumerator? _readerColumns;
private readonly Func _keySelector;
private readonly Func _keyIdentifier;
- private readonly IReadOnlyList _keyIdentifierValueComparers;
+ private readonly IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Type _contextType;
private readonly IDiagnosticsLogger _queryLogger;
diff --git a/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs b/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs
index c8e21e4f499..7c610c47b3a 100644
--- a/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs
+++ b/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs
@@ -20,7 +20,7 @@ public class GroupBySplitQueryingEnumerable
private readonly IReadOnlyList? _readerColumns;
private readonly Func _keySelector;
private readonly Func _keyIdentifier;
- private readonly IReadOnlyList _keyIdentifierValueComparers;
+ private readonly IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Action? _relatedDataLoaders;
private readonly Func? _relatedDataLoadersAsync;
@@ -42,7 +42,7 @@ public GroupBySplitQueryingEnumerable(
IReadOnlyList? readerColumns,
Func keySelector,
Func keyIdentifier,
- IReadOnlyList keyIdentifierValueComparers,
+ IReadOnlyList> keyIdentifierValueComparers,
Func elementSelector,
Action? relatedDataLoaders,
Func? relatedDataLoadersAsync,
@@ -145,12 +145,12 @@ IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
}
- private static bool CompareIdentifiers(IReadOnlyList valueComparers, object[] left, object[] right)
+ 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.
for (var i = 0; i < left.Length; i++)
{
- if (!valueComparers[i].Equals(left[i], right[i]))
+ if (!valueComparers[i](left[i], right[i]))
{
return false;
}
@@ -166,7 +166,7 @@ private sealed class Enumerator : IEnumerator>
private readonly IReadOnlyList? _readerColumns;
private readonly Func _keySelector;
private readonly Func _keyIdentifier;
- private readonly IReadOnlyList _keyIdentifierValueComparers;
+ private readonly IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Action? _relatedDataLoaders;
private readonly Type _contextType;
@@ -340,7 +340,7 @@ private sealed class AsyncEnumerator : IAsyncEnumerator? _readerColumns;
private readonly Func _keySelector;
private readonly Func _keyIdentifier;
- private readonly IReadOnlyList _keyIdentifierValueComparers;
+ private readonly IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Func? _relatedDataLoaders;
private readonly Type _contextType;
diff --git a/src/EFCore.Relational/Query/Internal/RelationalLiftableConstantExpressionDependencies.cs b/src/EFCore.Relational/Query/Internal/RelationalLiftableConstantExpressionDependencies.cs
new file mode 100644
index 00000000000..a4babebadaf
--- /dev/null
+++ b/src/EFCore.Relational/Query/Internal/RelationalLiftableConstantExpressionDependencies.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.
+
+namespace Microsoft.EntityFrameworkCore.Query.Internal;
+
+///
+///
+/// Service dependencies parameter class for
+///
+///
+/// This type is typically used by database providers (and other extensions). It is generally
+/// not used in application code.
+///
+///
+///
+///
+/// Do not construct instances of this class directly from either provider or application code as the
+/// constructor signature may change as new dependencies are added. Instead, use this type in
+/// your constructor so that an instance will be created and injected automatically by the
+/// dependency injection container. To create an instance with some dependent services replaced,
+/// first resolve the object from the dependency injection container, then replace selected
+/// services using the C# 'with' operator. Do not call the constructor at any point in this process.
+///
+///
+/// The service lifetime is . This means a single instance
+/// is used by many instances. The implementation must be thread-safe.
+/// This service cannot depend on services registered as .
+///
+///
+public sealed record RelationalLiftableConstantExpressionDependencies
+{
+}
diff --git a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs
index 2d9ef438560..42e34d8c4c5 100644
--- a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs
@@ -653,7 +653,13 @@ private ProjectionBindingExpression AddClientProjection(Expression expression, T
return new ProjectionBindingExpression(_selectExpression, existingIndex, type);
}
- private static T GetParameterValue(QueryContext queryContext, string parameterName)
+ ///
+ /// 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 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..560179079fe 100644
--- a/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
+++ b/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
@@ -6,6 +6,40 @@
namespace Microsoft.EntityFrameworkCore.Query.Internal;
+///
+/// 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 static class SingleQueryingEnumerable
+{
+ ///
+ /// 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 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/RelationalExpressionQuotingUtilities.cs b/src/EFCore.Relational/Query/RelationalExpressionQuotingUtilities.cs
index 699ad327ab3..612bf93928e 100644
--- a/src/EFCore.Relational/Query/RelationalExpressionQuotingUtilities.cs
+++ b/src/EFCore.Relational/Query/RelationalExpressionQuotingUtilities.cs
@@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Query;
///
/// Utilities used for implementing .
///
-[Experimental("EF1003")]
+[Experimental(EFDiagnostics.PrecompiledQueryExperimental)]
public static class RelationalExpressionQuotingUtilities
{
private static readonly ParameterExpression RelationalModelParameter
diff --git a/src/EFCore.Relational/Query/RelationalLiftableConstantFactory.cs b/src/EFCore.Relational/Query/RelationalLiftableConstantFactory.cs
new file mode 100644
index 00000000000..17b22c4b223
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalLiftableConstantFactory.cs
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.EntityFrameworkCore.Query.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+///
+/// This is an experimental API used by the Entity Framework Core feature and it is 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.
+///
+[Experimental(EFDiagnostics.PrecompiledQueryExperimental)]
+public class RelationalLiftableConstantFactory : LiftableConstantFactory, IRelationalLiftableConstantFactory
+{
+ ///
+ /// This is an experimental API used by the Entity Framework Core feature and it is 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 RelationalLiftableConstantFactory(
+#pragma warning disable EF1001 // Internal EF Core API usage.
+ LiftableConstantExpressionDependencies dependencies,
+#pragma warning restore EF1001 // Internal EF Core API usage.
+ RelationalLiftableConstantExpressionDependencies relationalDependencies)
+ : base(dependencies)
+ {
+ RelationalDependencies = relationalDependencies;
+ }
+
+ ///
+ /// This is an experimental API used by the Entity Framework Core feature and it is 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 virtual RelationalLiftableConstantExpressionDependencies RelationalDependencies { get; }
+
+ ///
+ /// This is an experimental API used by the Entity Framework Core feature and it is 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 virtual LiftableConstantExpression CreateLiftableConstant(
+ ConstantExpression originalExpression,
+ Expression> resolverExpression,
+ string variableName,
+ Type type)
+ => new(originalExpression, 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..1e942372ab0
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalLiftableConstantProcessor.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+///
+/// This is an experimental API used by the Entity Framework Core feature and it is 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.
+///
+[Experimental(EFDiagnostics.PrecompiledQueryExperimental)]
+public class RelationalLiftableConstantProcessor : LiftableConstantProcessor
+{
+ private readonly RelationalMaterializerLiftableConstantContext _relationalMaterializerLiftableConstantContext;
+
+ ///
+ /// This is an experimental API used by the Entity Framework Core feature and it is 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 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..e54e2b507a2
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalMaterializerLiftableConstantContext.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+///
+/// This is an experimental API used by the Entity Framework Core feature and it is 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.
+///
+[Experimental(EFDiagnostics.PrecompiledQueryExperimental)]
+public record RelationalMaterializerLiftableConstantContext(
+ ShapedQueryCompilingExpressionVisitorDependencies Dependencies,
+ RelationalShapedQueryCompilingExpressionVisitorDependencies RelationalDependencies)
+ : MaterializerLiftableConstantContext(Dependencies);
diff --git a/src/EFCore.Relational/Query/RelationalQueryCompilationContext.cs b/src/EFCore.Relational/Query/RelationalQueryCompilationContext.cs
index 970c8eda4b2..113868cc4d0 100644
--- a/src/EFCore.Relational/Query/RelationalQueryCompilationContext.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryCompilationContext.cs
@@ -49,4 +49,7 @@ public RelationalQueryCompilationContext(
/// A manager for SQL aliases, capable of generate uniquified table aliases.
///
public virtual SqlAliasManager SqlAliasManager { get; }
+
+ ///
+ public override bool SupportsPrecompiledQuery => true;
}
diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs
index 33b01fd885a..07df5d5c415 100644
--- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs
+++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections;
using System.Runtime.CompilerServices;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Internal;
@@ -11,7 +12,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))!;
@@ -71,8 +79,15 @@ private static readonly MethodInfo MaterializeJsonEntityCollectionMethodInfo
private static readonly MethodInfo InverseCollectionFixupMethod
= typeof(ShaperProcessingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(InverseCollectionFixup))!;
+ ///
+ /// 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.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static TValue ThrowReadValueException(
+ [EntityFrameworkInternal]
+ public static TValue ThrowReadValueException(
Exception exception,
object? value,
Type expectedType,
@@ -122,7 +137,14 @@ private static TValue ThrowExtractJsonPropertyException(Exception except
exception);
}
- private static void IncludeReference(
+ ///
+ /// 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 IncludeReference(
QueryContext queryContext,
TEntity entity,
TIncludedEntity? relatedEntity,
@@ -160,7 +182,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 +230,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 +245,9 @@ private static void PopulateIncludeCollection
Func parentIdentifier,
Func outerIdentifier,
Func selfIdentifier,
- IReadOnlyList parentIdentifierValueComparers,
- IReadOnlyList outerIdentifierValueComparers,
- IReadOnlyList selfIdentifierValueComparers,
+ IReadOnlyList> parentIdentifierValueComparers,
+ IReadOnlyList> outerIdentifierValueComparers,
+ IReadOnlyList> selfIdentifierValueComparers,
Func innerShaper,
INavigationBase? inverseNavigation,
Action fixup,
@@ -319,7 +355,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 +401,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 +417,7 @@ private static void PopulateSplitIncludeCollection childIdentifier,
- IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
Func innerShaper,
Action? relatedDataLoaders,
INavigationBase? inverseNavigation,
@@ -442,7 +492,14 @@ static RelationalDataReader InitializeReader(
}
}
- private static async Task PopulateSplitIncludeCollectionAsync(
+ ///
+ /// 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 async Task PopulateSplitIncludeCollectionAsync(
int collectionId,
RelationalQueryContext queryContext,
IExecutionStrategy executionStrategy,
@@ -451,7 +508,7 @@ private static async Task PopulateSplitIncludeCollectionAsync childIdentifier,
- IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
Func innerShaper,
Func? relatedDataLoaders,
INavigationBase? inverseNavigation,
@@ -538,7 +595,14 @@ static async Task InitializeReaderAsync(
}
}
- private static TCollection InitializeCollection(
+ ///
+ /// 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 TCollection InitializeCollection(
int collectionId,
QueryContext queryContext,
DbDataReader dbDataReader,
@@ -560,7 +624,14 @@ private static TCollection InitializeCollection(
return (TCollection)collection;
}
- private static void PopulateCollection(
+ ///
+ /// 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 PopulateCollection(
int collectionId,
QueryContext queryContext,
DbDataReader dbDataReader,
@@ -568,9 +639,9 @@ private static void PopulateCollection(
Func parentIdentifier,
Func outerIdentifier,
Func selfIdentifier,
- IReadOnlyList parentIdentifierValueComparers,
- IReadOnlyList outerIdentifierValueComparers,
- IReadOnlyList selfIdentifierValueComparers,
+ IReadOnlyList> parentIdentifierValueComparers,
+ IReadOnlyList> outerIdentifierValueComparers,
+ IReadOnlyList> selfIdentifierValueComparers,
Func innerShaper)
where TRelatedEntity : TElement
where TCollection : class, ICollection
@@ -590,8 +661,8 @@ private static void PopulateCollection(
}
if (!CompareIdentifiers(
- outerIdentifierValueComparers,
- outerIdentifier(queryContext, dbDataReader), collectionMaterializationContext.OuterIdentifier))
+ outerIdentifierValueComparers,
+ outerIdentifier(queryContext, dbDataReader), collectionMaterializationContext.OuterIdentifier))
{
// Outer changed so collection has ended. Materialize last element.
GenerateCurrentElementIfPending();
@@ -616,8 +687,8 @@ private static void PopulateCollection(
if (collectionMaterializationContext.SelfIdentifier != null)
{
if (CompareIdentifiers(
- selfIdentifierValueComparers,
- innerKey, collectionMaterializationContext.SelfIdentifier))
+ selfIdentifierValueComparers,
+ innerKey, collectionMaterializationContext.SelfIdentifier))
{
// repeated row for current element
// If it is pending materialization then it may have nested elements
@@ -673,7 +744,14 @@ void GenerateCurrentElementIfPending()
}
}
- private static TCollection InitializeSplitCollection(
+ ///
+ /// 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 TCollection InitializeSplitCollection(
int collectionId,
QueryContext queryContext,
DbDataReader parentDataReader,
@@ -691,7 +769,14 @@ private static TCollection InitializeSplitCollection(
return (TCollection)collection;
}
- private static void PopulateSplitCollection(
+ ///
+ /// 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 PopulateSplitCollection(
int collectionId,
RelationalQueryContext queryContext,
IExecutionStrategy executionStrategy,
@@ -700,7 +785,7 @@ private static void PopulateSplitCollection childIdentifier,
- IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
Func innerShaper,
Action? relatedDataLoaders)
where TRelatedEntity : TElement
@@ -770,7 +855,14 @@ static RelationalDataReader InitializeReader(
dataReaderContext.HasNext = false;
}
- private static async Task PopulateSplitCollectionAsync(
+ ///
+ /// 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 async Task PopulateSplitCollectionAsync(
int collectionId,
RelationalQueryContext queryContext,
IExecutionStrategy executionStrategy,
@@ -779,7 +871,7 @@ private static async Task PopulateSplitCollectionAsync childIdentifier,
- IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
Func innerShaper,
Func? relatedDataLoaders)
where TRelatedEntity : TElement
@@ -861,7 +953,14 @@ static async Task InitializeReaderAsync(
dataReaderContext.HasNext = false;
}
- private static TEntity? MaterializeJsonEntity(
+ ///
+ /// 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 TEntity? MaterializeJsonEntity(
QueryContext queryContext,
object[] keyPropertyValues,
JsonReaderData? jsonReaderData,
@@ -900,7 +999,14 @@ static async Task InitializeReaderAsync(
return result;
}
- private static TResult? MaterializeJsonEntityCollection(
+ ///
+ /// 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 TResult? MaterializeJsonEntityCollection(
QueryContext queryContext,
object[] keyPropertyValues,
JsonReaderData? jsonReaderData,
@@ -967,7 +1073,14 @@ static async Task InitializeReaderAsync(
return result;
}
- private static void IncludeJsonEntityReference(
+ ///
+ /// 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 IncludeJsonEntityReference(
QueryContext queryContext,
object[] keyPropertyValues,
JsonReaderData? jsonReaderData,
@@ -991,7 +1104,14 @@ private static void IncludeJsonEntityReference(
+ ///
+ /// 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 IncludeJsonEntityCollection(
QueryContext queryContext,
object[] keyPropertyValues,
JsonReaderData? jsonReaderData,
@@ -1058,7 +1178,31 @@ private static void IncludeJsonEntityCollection[] taskFactories)
+ ///
+ /// 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 bool Any(IEnumerable source)
+ {
+ foreach (var _ in source)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// 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 async Task TaskAwaiter(Func[] taskFactories)
{
for (var i = 0; i < taskFactories.Length; i++)
{
@@ -1066,12 +1210,12 @@ private static async Task TaskAwaiter(Func[] taskFactories)
}
}
- private static bool CompareIdentifiers(IReadOnlyList valueComparers, object[] left, object[] right)
+ 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.
for (var i = 0; i < left.Length; i++)
{
- if (!valueComparers[i].Equals(left[i], right[i]))
+ if (!valueComparers[i](left[i], right[i]))
{
return false;
}
diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs
index b6f5aa95364..f0674ecd9bd 100644
--- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs
@@ -2,11 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
using Microsoft.EntityFrameworkCore.Storage.Json;
using static System.Linq.Expressions.Expression;
@@ -14,7 +16,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
@@ -22,6 +30,12 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
private static readonly MethodInfo IsDbNullMethod =
typeof(DbDataReader).GetRuntimeMethod(nameof(DbDataReader.IsDBNull), [typeof(int)])!;
+ ///
+ /// 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 static readonly MethodInfo GetFieldValueMethod =
typeof(DbDataReader).GetRuntimeMethod(nameof(DbDataReader.GetFieldValue), [typeof(int)])!;
@@ -77,6 +91,9 @@ private static readonly MethodInfo Utf8JsonReaderGetStringMethod
private static readonly MethodInfo EnumParseMethodInfo
= typeof(Enum).GetMethod(nameof(Enum.Parse), [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;
@@ -160,6 +177,14 @@ private readonly Dictionary
///
private readonly Dictionary _jsonArrayNonConstantElementAccessMap = new();
+ private readonly DisplayClassConstantFixer _displayClassConstantFixer = new();
+
+ ///
+ /// 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 ShaperProcessingExpressionVisitor(
RelationalShapedQueryCompilingExpressionVisitor parentVisitor,
SelectExpression selectExpression,
@@ -172,7 +197,6 @@ public ShaperProcessingExpressionVisitor(
_resultCoordinatorParameter = Parameter(
splitQuery ? typeof(SplitQueryResultCoordinator) : typeof(SingleQueryResultCoordinator), "resultCoordinator");
_executionStrategyParameter = splitQuery ? Parameter(typeof(IExecutionStrategy), "executionStrategy") : null;
-
_selectExpression = selectExpression;
_tags = tags;
_dataReaderParameter = Parameter(typeof(DbDataReader), "dataReader");
@@ -247,10 +271,16 @@ private ShaperProcessingExpressionVisitor(
_selectExpression.ApplyTags(_tags);
}
+ ///
+ /// 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 LambdaExpression ProcessRelationalGroupingResult(
RelationalGroupByResultExpression relationalGroupByResultExpression,
- out RelationalCommandCache relationalCommandCache,
- out IReadOnlyList? readerColumns,
+ out Expression relationalCommandCache,
+ out Func readerColumns,
out LambdaExpression keySelector,
out LambdaExpression keyIdentifier,
out LambdaExpression? relatedDataLoaders,
@@ -277,10 +307,16 @@ public LambdaExpression ProcessRelationalGroupingResult(
ref collectionId);
}
+ ///
+ /// 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 LambdaExpression ProcessShaper(
Expression shaperExpression,
- out RelationalCommandCache? relationalCommandCache,
- out IReadOnlyList? readerColumns,
+ out Expression relationalCommandCache,
+ out Func readerColumns,
out LambdaExpression? relatedDataLoaders,
ref int collectionId)
{
@@ -293,13 +329,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 +351,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 +434,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;
@@ -428,6 +449,12 @@ public LambdaExpression ProcessShaper(
}
}
+ ///
+ /// 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.
+ ///
protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
switch (binaryExpression)
@@ -452,8 +479,18 @@ 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.QueryCompilationContext.SupportsPrecompiledQuery
+ ? _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
+ Constant(ValueBuffer.Empty),
+ _ => ValueBuffer.Empty,
+ "emptyValueBuffer",
+ typeof(ValueBuffer))
+ : Constant(ValueBuffer.Empty),
+ newExpression.Arguments[1]
+ });
return Assign(binaryExpression.Left, updatedExpression);
}
@@ -465,7 +502,17 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
_jsonMaterializationContextToJsonReaderDataAndKeyValuesParameterMapping[parameterExpression] = mappedParameter;
var updatedExpression = newExpression.Update(
- new[] { Constant(ValueBuffer.Empty), newExpression.Arguments[1] });
+ new[]
+ {
+ _parentVisitor.QueryCompilationContext.SupportsPrecompiledQuery
+ ? _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
+ Constant(ValueBuffer.Empty),
+ _ => ValueBuffer.Empty,
+ "emptyValueBuffer",
+ typeof(ValueBuffer))
+ : Constant(ValueBuffer.Empty),
+ newExpression.Arguments[1]
+ });
return Assign(binaryExpression.Left, updatedExpression);
}
@@ -497,6 +544,12 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
return base.VisitBinary(binaryExpression);
}
+ ///
+ /// 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.
+ ///
protected override Expression VisitExtension(Expression extensionExpression)
{
switch (extensionExpression)
@@ -597,7 +650,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 +771,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 +822,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 +852,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,
+ _parentVisitor.QueryCompilationContext.SupportsPrecompiledQuery
+ ? _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
+ Constant(navigation),
+ LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(navigation),
+ navigation.Name + "Navigation",
+ typeof(INavigationBase))
+ : Constant(navigation),
+ navigation.IsShadowProperty()
+ ? Constant(null, typeof(IClrCollectionAccessor))
+ : _parentVisitor.QueryCompilationContext.SupportsPrecompiledQuery
+ ? _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
+ Constant(navigation.GetCollectionAccessor(), typeof(IClrCollectionAccessor)),
+ LiftableConstantExpressionHelpers.BuildClrCollectionAccessorLambda(navigation),
+ navigation.Name + "NavigationCollectionAccessor",
+ typeof(IClrCollectionAccessor))
+ : Constant(navigation.GetCollectionAccessor(), 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