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/InMemoryExpressionTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs
index 322ab4e6fcb..c7c6b3c413d 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs
@@ -341,6 +341,10 @@ static bool IsTypeConstant(Expression expression, out Type? type)
}
}
+ //private static readonly MethodInfo PropertyGetValueConverterMethod
+ // = typeof(IReadOnlyProperty).GetMethod(nameof(IReadOnlyProperty.GetValueComparer))!;
+ // //= typeof(IProperty).GetMethod(nameof(IProperty.GetValueComparer))!;
+
private static bool TryUseComparer(
Expression? newLeft,
Expression? newRight,
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs
index 5d833b29426..68495f09d99 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;
@@ -896,6 +895,9 @@ private static Expression GetGroupingKey(Expression key, List groupi
}
}
+ //private static readonly MethodInfo PropertyGetValueConverterMethod
+ // = typeof(IReadOnlyProperty).GetMethod(nameof(IReadOnlyProperty.GetValueComparer))!;
+
private Expression AddJoin(
InMemoryQueryExpression innerQueryExpression,
LambdaExpression? outerKeySelector,
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 77feb85648d..aa4bfcfd424 100644
--- a/src/EFCore.Relational/EFCore.Relational.csproj
+++ b/src/EFCore.Relational/EFCore.Relational.csproj
@@ -8,7 +8,7 @@
Microsoft.EntityFrameworkCoretruetrue
- $(NoWarn);EF1003
+ $(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 class EntityFrameworkRelationalServicesBuilder : EntityFrameworkServicesB
{ 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..c2a7c1e86ff
--- /dev/null
+++ b/src/EFCore.Relational/Query/IRelationalLiftableConstantFactory.cs
@@ -0,0 +1,19 @@
+// 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;
+
+///
+/// TODO
+///
+public interface IRelationalLiftableConstantFactory : ILiftableConstantFactory
+{
+ ///
+ /// TODO
+ ///
+ 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..92d1cb03cfb 100644
--- a/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs
+++ b/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs
@@ -20,7 +20,8 @@ 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 IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Type _contextType;
private readonly IDiagnosticsLogger _queryLogger;
@@ -40,7 +41,8 @@ public class GroupBySingleQueryingEnumerable
IReadOnlyList? readerColumns,
Func keySelector,
Func keyIdentifier,
- IReadOnlyList keyIdentifierValueComparers,
+ //IReadOnlyList keyIdentifierValueComparers,
+ IReadOnlyList> keyIdentifierValueComparers,
Func elementSelector,
Type contextType,
bool standAloneStateManager,
@@ -139,12 +141,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 +162,8 @@ 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 IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Type _contextType;
private readonly IDiagnosticsLogger _queryLogger;
@@ -344,7 +347,8 @@ private sealed class AsyncEnumerator : IAsyncEnumerator? _readerColumns;
private readonly Func _keySelector;
private readonly Func _keyIdentifier;
- private readonly IReadOnlyList _keyIdentifierValueComparers;
+ //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..f614dbf0931 100644
--- a/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs
+++ b/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs
@@ -20,7 +20,8 @@ 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 IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Action? _relatedDataLoaders;
private readonly Func? _relatedDataLoadersAsync;
@@ -42,7 +43,8 @@ public class GroupBySplitQueryingEnumerable
IReadOnlyList? readerColumns,
Func keySelector,
Func keyIdentifier,
- IReadOnlyList keyIdentifierValueComparers,
+ //IReadOnlyList keyIdentifierValueComparers,
+ IReadOnlyList> keyIdentifierValueComparers,
Func elementSelector,
Action? relatedDataLoaders,
Func? relatedDataLoadersAsync,
@@ -145,12 +147,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 +168,8 @@ 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 IReadOnlyList> _keyIdentifierValueComparers;
private readonly Func _elementSelector;
private readonly Action? _relatedDataLoaders;
private readonly Type _contextType;
@@ -340,7 +343,8 @@ private sealed class AsyncEnumerator : IAsyncEnumerator? _readerColumns;
private readonly Func _keySelector;
private readonly Func _keyIdentifier;
- private readonly IReadOnlyList _keyIdentifierValueComparers;
+ //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..24a040293ee 100644
--- a/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
+++ b/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
@@ -6,6 +6,37 @@
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
+{
+ ///
+ /// TODO
+ ///
+ 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..bab8e3d63d6
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalLiftableConstantFactory.cs
@@ -0,0 +1,40 @@
+// 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;
+
+///
+/// TODO
+///
+public class RelationalLiftableConstantFactory : LiftableConstantFactory, IRelationalLiftableConstantFactory
+{
+ ///
+ /// TODO
+ ///
+ 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;
+ }
+
+ ///
+ /// TODO
+ ///
+ public virtual RelationalLiftableConstantExpressionDependencies RelationalDependencies { get; }
+
+ ///
+ /// TODO
+ ///
+ 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..b8fab3919bf
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalLiftableConstantProcessor.cs
@@ -0,0 +1,39 @@
+// 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
+
+///
+/// TODO
+///
+public class RelationalLiftableConstantProcessor : LiftableConstantProcessor
+{
+ private readonly RelationalMaterializerLiftableConstantContext _relationalMaterializerLiftableConstantContext;
+
+ ///
+ /// TODO
+ ///
+ 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..815578c836a
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalMaterializerLiftableConstantContext.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;
+
+///
+/// TODO
+///
+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 d4bdfe0bd94..e0e3628ce8f 100644
--- a/src/EFCore.Relational/Query/RelationalQueryCompilationContext.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryCompilationContext.cs
@@ -47,4 +47,7 @@ public class RelationalQueryCompilationContext : QueryCompilationContext
/// 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..b7254223fd6 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 sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
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 TValue ThrowExtractJsonPropertyException(Exception except
}
}
- 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 TValue ThrowExtractJsonPropertyException(Exception except
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,12 @@ private static TValue ThrowExtractJsonPropertyException(Exception except
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,
@@ -319,7 +358,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 +404,14 @@ void GenerateCurrentElementIfPending()
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 +420,8 @@ void GenerateCurrentElementIfPending()
bool detailedErrorsEnabled,
SplitQueryResultCoordinator resultCoordinator,
Func childIdentifier,
- IReadOnlyList identifierValueComparers,
+ // IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
Func innerShaper,
Action? relatedDataLoaders,
INavigationBase? inverseNavigation,
@@ -442,7 +496,14 @@ void GenerateCurrentElementIfPending()
}
}
- 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 +512,8 @@ void GenerateCurrentElementIfPending()
bool detailedErrorsEnabled,
SplitQueryResultCoordinator resultCoordinator,
Func childIdentifier,
- IReadOnlyList identifierValueComparers,
+ // IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
Func innerShaper,
Func? relatedDataLoaders,
INavigationBase? inverseNavigation,
@@ -538,7 +600,14 @@ void GenerateCurrentElementIfPending()
}
}
- 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 +629,14 @@ void GenerateCurrentElementIfPending()
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 +644,12 @@ void GenerateCurrentElementIfPending()
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)
where TRelatedEntity : TElement
where TCollection : class, ICollection
@@ -590,8 +669,8 @@ void GenerateCurrentElementIfPending()
}
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 +695,8 @@ void GenerateCurrentElementIfPending()
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 +752,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 +777,14 @@ void GenerateCurrentElementIfPending()
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 +793,8 @@ void GenerateCurrentElementIfPending()
bool detailedErrorsEnabled,
SplitQueryResultCoordinator resultCoordinator,
Func childIdentifier,
- IReadOnlyList identifierValueComparers,
+ //IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
Func innerShaper,
Action? relatedDataLoaders)
where TRelatedEntity : TElement
@@ -770,7 +864,14 @@ void GenerateCurrentElementIfPending()
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 +880,8 @@ void GenerateCurrentElementIfPending()
bool detailedErrorsEnabled,
SplitQueryResultCoordinator resultCoordinator,
Func childIdentifier,
- IReadOnlyList identifierValueComparers,
+ //IReadOnlyList identifierValueComparers,
+ IReadOnlyList> identifierValueComparers,
Func innerShaper,
Func? relatedDataLoaders)
where TRelatedEntity : TElement
@@ -861,7 +963,14 @@ void GenerateCurrentElementIfPending()
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 +1009,14 @@ void GenerateCurrentElementIfPending()
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 +1083,14 @@ void GenerateCurrentElementIfPending()
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 +1114,14 @@ void GenerateCurrentElementIfPending()
}
}
- private static void IncludeJsonEntityCollection(
+ ///
+ /// 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 +1188,31 @@ void GenerateCurrentElementIfPending()
manager.CaptureState();
}
- private static async Task TaskAwaiter(Func[] 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 +1220,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..bb65159a64e 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,9 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
private static readonly MethodInfo IsDbNullMethod =
typeof(DbDataReader).GetRuntimeMethod(nameof(DbDataReader.IsDBNull), [typeof(int)])!;
+ ///
+ /// TODO
+ ///
public static readonly MethodInfo GetFieldValueMethod =
typeof(DbDataReader).GetRuntimeMethod(nameof(DbDataReader.GetFieldValue), [typeof(int)])!;
@@ -77,6 +88,9 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
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 +174,12 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
///
private readonly Dictionary _jsonArrayNonConstantElementAccessMap = 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 +192,6 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
_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 +266,17 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
_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 IReadOnlyList? readerColumns,
out LambdaExpression keySelector,
out LambdaExpression keyIdentifier,
out LambdaExpression? relatedDataLoaders,
@@ -277,10 +303,17 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
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 IReadOnlyList? readerColumns,
out LambdaExpression? relatedDataLoaders,
ref int collectionId)
{
@@ -293,13 +326,8 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
_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 +348,9 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
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 +431,9 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
}
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 +446,12 @@ private sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisit
}
}
+ ///
+ /// 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 +476,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 +499,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 +541,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 +647,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 +768,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 +819,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 +849,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