From 74eb726faa80faa66a90d6faa9c38b4b786f1f74 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 17 Jul 2025 02:12:32 +0000 Subject: [PATCH] =?UTF-8?q?[efcore]=20Source=20update=20bc5def3=20?= =?UTF-8?q?=E2=86=92=2074068d2=20Diff:=20https://github.com/dotnet/efcore/?= =?UTF-8?q?compare/bc5def338228c7ec5ab5d46f6e9042439270f438..74068d2036ea9?= =?UTF-8?q?db2454d62865eef3a1414622538?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: https://github.com/dotnet/efcore/commit/bc5def338228c7ec5ab5d46f6e9042439270f438 To: https://github.com/dotnet/efcore/commit/74068d2036ea9db2454d62865eef3a1414622538 [[ commit created by automation ]] --- src/efcore/eng/Version.Details.xml | 74 ++++----- src/efcore/eng/Versions.props | 32 ++-- src/efcore/global.json | 4 +- .../src/EFCore.Relational/EFExtensions.cs | 6 +- .../RelationalDbContextOptionsBuilder.cs | 8 +- .../RelationalOptionsExtension.cs | 8 +- .../GroupBySingleQueryingEnumerable.cs | 2 +- .../GroupBySplitQueryingEnumerable.cs | 2 +- .../Query/Internal/RelationalCommandCache.cs | 4 +- .../Internal/RelationalParameterProcessor.cs | 2 +- .../Internal/SingleQueryingEnumerable.cs | 2 +- ...nalParameterBasedSqlProcessorParameters.cs | 8 +- ...yableMethodTranslatingExpressionVisitor.cs | 47 +++--- ...alShapedQueryCompilingExpressionVisitor.cs | 10 +- ...lationalSqlTranslatingExpressionVisitor.cs | 4 +- .../SqlExpressions/SqlParameterExpression.cs | 14 +- .../Query/SqlNullabilityProcessor.cs | 37 ++--- .../SqlServerSqlNullabilityProcessor.cs | 6 +- .../Storage/Internal/SqliteDatabaseCreator.cs | 4 +- .../ParameterTranslationMode.cs} | 32 ++-- .../EFCore/Properties/CoreStrings.Designer.cs | 30 ++-- .../src/EFCore/Properties/CoreStrings.resx | 13 +- .../Internal/ExpressionTreeFuncletizer.cs | 38 +++-- ...yableMethodNormalizingExpressionVisitor.cs | 21 ++- .../EFCore/Query/QueryParameterExpression.cs | 31 ++-- .../Query/NorthwindWhereQueryCosmosTest.cs | 4 +- ...HocMiscellaneousQueryRelationalTestBase.cs | 8 +- ...itiveCollectionsQueryRelationalTestBase.cs | 119 ++++++-------- .../NorthwindWhereQueryRelationalTestBase.cs | 14 ++ .../Query/NorthwindWhereQueryTestBase.cs | 4 +- .../AdHocMiscellaneousQuerySqlServerTest.cs | 2 +- ...dPrimitiveCollectionsQuerySqlServerTest.cs | 151 +++++++++++------ .../Query/NorthwindWhereQuerySqlServerTest.cs | 7 + .../AdHocMiscellaneousQuerySqliteTest.cs | 2 +- ...aredPrimitiveCollectionsQuerySqliteTest.cs | 153 ++++++++++++------ src/source-manifest.json | 52 +++--- 36 files changed, 518 insertions(+), 437 deletions(-) rename src/efcore/src/{EFCore.Relational/ParameterizedCollectionMode.cs => EFCore/ParameterTranslationMode.cs} (88%) diff --git a/src/efcore/eng/Version.Details.xml b/src/efcore/eng/Version.Details.xml index 748a4119e16..0090de09b77 100644 --- a/src/efcore/eng/Version.Details.xml +++ b/src/efcore/eng/Version.Details.xml @@ -1,80 +1,80 @@ - + - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b - + https://github.com/dotnet/dotnet - 86117ef4343bab700dab871c416e55b22848cd52 + 78061f4bcc414fa2054be6237b1fd3813d8edf6b diff --git a/src/efcore/eng/Versions.props b/src/efcore/eng/Versions.props index e0dd2995e1c..8d7ef6a18e8 100644 --- a/src/efcore/eng/Versions.props +++ b/src/efcore/eng/Versions.props @@ -16,24 +16,24 @@ False - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 - 10.0.0-preview.7.25360.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 + 10.0.0-preview.7.25365.101 - 10.0.0-beta.25360.101 + 10.0.0-beta.25365.101 17.14.8 diff --git a/src/efcore/global.json b/src/efcore/global.json index 81b3c4ca1fa..b7232466c10 100644 --- a/src/efcore/global.json +++ b/src/efcore/global.json @@ -18,7 +18,7 @@ } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25360.101", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25360.101" + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25365.101", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25365.101" } } diff --git a/src/efcore/src/EFCore.Relational/EFExtensions.cs b/src/efcore/src/EFCore.Relational/EFExtensions.cs index c2677cb8706..c790c6256f2 100644 --- a/src/efcore/src/EFCore.Relational/EFExtensions.cs +++ b/src/efcore/src/EFCore.Relational/EFExtensions.cs @@ -1,6 +1,8 @@ // 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; + namespace Microsoft.EntityFrameworkCore; /// @@ -25,10 +27,10 @@ public static class EFExtensions /// Within the context of an EF LINQ query, forces its argument to be inserted into the query as a multiple parameter expressions. /// /// Note that this is a static method accessed through the top-level static type. - /// The type of collection element. + /// The type of collection. /// The collection to be integrated as parameters into the query. /// The same value for further use in the query. - public static IEnumerable MultipleParameters(IEnumerable argument) + public static TSource MultipleParameters(TSource argument) where TSource : IEnumerable => throw new InvalidOperationException(RelationalStrings.EFMultipleParametersInvoked); } } diff --git a/src/efcore/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs b/src/efcore/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs index 269a68bfb0a..01935a24036 100644 --- a/src/efcore/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs +++ b/src/efcore/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs @@ -179,7 +179,7 @@ public virtual TBuilder ExecutionStrategy( /// [Obsolete("Use UseParameterizedCollectionMode instead.")] public virtual TBuilder TranslateParameterizedCollectionsToConstants() - => UseParameterizedCollectionMode(ParameterizedCollectionMode.Constants); + => UseParameterizedCollectionMode(ParameterTranslationMode.Constant); /// /// Configures the context to translate parameterized collections to a single array-like parameter. @@ -202,13 +202,13 @@ public virtual TBuilder TranslateParameterizedCollectionsToConstants() /// [Obsolete("Use UseParameterizedCollectionMode instead.")] public virtual TBuilder TranslateParameterizedCollectionsToParameters() - => UseParameterizedCollectionMode(ParameterizedCollectionMode.Parameter); + => UseParameterizedCollectionMode(ParameterTranslationMode.Parameter); /// - /// Configures the to use when translating parameterized collections. + /// Configures the mode to use when translating parameterized collections. /// /// The same builder instance so that multiple calls can be chained. - public virtual TBuilder UseParameterizedCollectionMode(ParameterizedCollectionMode parameterizedCollectionMode) + public virtual TBuilder UseParameterizedCollectionMode(ParameterTranslationMode parameterizedCollectionMode) => WithOption(e => (TExtension)e.WithUseParameterizedCollectionMode(parameterizedCollectionMode)); /// diff --git a/src/efcore/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs b/src/efcore/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs index e2cc09b80c8..a052b74c1eb 100644 --- a/src/efcore/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs +++ b/src/efcore/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs @@ -36,7 +36,7 @@ public abstract class RelationalOptionsExtension : IDbContextOptionsExtension private string? _migrationsHistoryTableName; private string? _migrationsHistoryTableSchema; private Func? _executionStrategyFactory; - private ParameterizedCollectionMode? _parameterizedCollectionMode; + private ParameterTranslationMode? _parameterizedCollectionMode; /// /// Creates a new set of options with everything set to default values. @@ -386,8 +386,8 @@ public virtual RelationalOptionsExtension WithExecutionStrategyFactory( /// /// Configured translation mode for parameterized collections. /// - public virtual ParameterizedCollectionMode ParameterizedCollectionMode - => _parameterizedCollectionMode ?? ParameterizedCollectionMode.MultipleParameters; + public virtual ParameterTranslationMode ParameterizedCollectionMode + => _parameterizedCollectionMode ?? ParameterTranslationMode.MultipleParameters; /// /// Creates a new instance with all options the same as for this instance, but with the given option changed. @@ -395,7 +395,7 @@ public virtual ParameterizedCollectionMode ParameterizedCollectionMode /// /// The option to change. public virtual RelationalOptionsExtension WithUseParameterizedCollectionMode( - ParameterizedCollectionMode parameterizedCollectionMode) + ParameterTranslationMode parameterizedCollectionMode) { var clone = Clone(); diff --git a/src/efcore/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs b/src/efcore/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs index af0eb423bff..24f47e554a4 100644 --- a/src/efcore/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs +++ b/src/efcore/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs @@ -145,7 +145,7 @@ public virtual DbCommand CreateDbCommand() _relationalQueryContext.Connection, _relationalQueryContext.Parameters, null, - null, + _relationalQueryContext.Context, null, CommandSource.LinqQuery), Guid.Empty, (DbCommandMethod)(-1)); diff --git a/src/efcore/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs b/src/efcore/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs index e8b67872908..1696922220b 100644 --- a/src/efcore/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs +++ b/src/efcore/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs @@ -155,7 +155,7 @@ public virtual DbCommand CreateDbCommand() _relationalQueryContext.Connection, _relationalQueryContext.Parameters, null, - null, + _relationalQueryContext.Context, null, CommandSource.LinqQuery), Guid.Empty, (DbCommandMethod)(-1)); diff --git a/src/efcore/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs b/src/efcore/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs index 4d19a2b3eaa..e7ba5c61764 100644 --- a/src/efcore/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs +++ b/src/efcore/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs @@ -35,13 +35,13 @@ public RelationalCommandCache( IRelationalParameterBasedSqlProcessorFactory relationalParameterBasedSqlProcessorFactory, Expression queryExpression, bool useRelationalNulls, - ParameterizedCollectionMode parameterizedCollectionMode) + ParameterTranslationMode collectionParameterTranslationMode) { _memoryCache = memoryCache; _querySqlGeneratorFactory = querySqlGeneratorFactory; _queryExpression = queryExpression; _relationalParameterBasedSqlProcessor = relationalParameterBasedSqlProcessorFactory.Create( - new RelationalParameterBasedSqlProcessorParameters(useRelationalNulls, parameterizedCollectionMode)); + new RelationalParameterBasedSqlProcessorParameters(useRelationalNulls, collectionParameterTranslationMode)); } /// diff --git a/src/efcore/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs b/src/efcore/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs index b955ccae286..f1ed4b087b1 100644 --- a/src/efcore/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs +++ b/src/efcore/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs @@ -128,7 +128,7 @@ private SqlParameterExpression VisitSqlParameter(SqlParameterExpression paramete uniquifiedName, parameter.Type, parameter.IsNullable, - parameter.ShouldBeConstantized, + parameter.TranslationMode, parameter.TypeMapping); return _sqlParameters[newParameter.InvariantName] = newParameter; diff --git a/src/efcore/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs b/src/efcore/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs index 5a79eb34b9e..85b999e82a0 100644 --- a/src/efcore/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs +++ b/src/efcore/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs @@ -129,7 +129,7 @@ public virtual DbCommand CreateDbCommand() _relationalQueryContext.Connection, _relationalQueryContext.Parameters, null, - null, + _relationalQueryContext.Context, null, CommandSource.LinqQuery), Guid.Empty, (DbCommandMethod)(-1)); diff --git a/src/efcore/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessorParameters.cs b/src/efcore/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessorParameters.cs index 415a8db6e54..d0bc742fd83 100644 --- a/src/efcore/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessorParameters.cs +++ b/src/efcore/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessorParameters.cs @@ -16,17 +16,17 @@ public sealed record RelationalParameterBasedSqlProcessorParameters /// /// Which parametrized collection translation mode should be used. /// - public ParameterizedCollectionMode ParameterizedCollectionMode { get; init; } + public ParameterTranslationMode CollectionParameterTranslationMode { get; init; } /// /// Creates a new instance of . /// /// A value indicating if relational nulls should be used. - /// Which translation mode should be used. + /// Which translation mode should be used. [EntityFrameworkInternal] - public RelationalParameterBasedSqlProcessorParameters(bool useRelationalNulls, ParameterizedCollectionMode parameterizedCollectionMode) + public RelationalParameterBasedSqlProcessorParameters(bool useRelationalNulls, ParameterTranslationMode collectionParameterTranslationMode) { UseRelationalNulls = useRelationalNulls; - ParameterizedCollectionMode = parameterizedCollectionMode; + CollectionParameterTranslationMode = collectionParameterTranslationMode; } } diff --git a/src/efcore/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/efcore/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 848973552c6..18bb222f2f1 100644 --- a/src/efcore/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/efcore/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -21,7 +21,7 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor : Que private readonly IRelationalTypeMappingSource _typeMappingSource; private readonly ISqlExpressionFactory _sqlExpressionFactory; private readonly bool _subquery; - private readonly ParameterizedCollectionMode _parameterizedCollectionMode; + private readonly ParameterTranslationMode _collectionParameterTranslationMode; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -64,7 +64,7 @@ public RelationalQueryableMethodTranslatingExpressionVisitor( _typeMappingSource = relationalDependencies.TypeMappingSource; _sqlExpressionFactory = sqlExpressionFactory; _subquery = false; - _parameterizedCollectionMode = RelationalOptionsExtension.Extract(queryCompilationContext.ContextOptions).ParameterizedCollectionMode; + _collectionParameterTranslationMode = RelationalOptionsExtension.Extract(queryCompilationContext.ContextOptions).ParameterizedCollectionMode; } /// @@ -90,7 +90,7 @@ protected RelationalQueryableMethodTranslatingExpressionVisitor( _typeMappingSource = parentVisitor._typeMappingSource; _sqlExpressionFactory = parentVisitor._sqlExpressionFactory; _subquery = true; - _parameterizedCollectionMode = RelationalOptionsExtension.Extract(parentVisitor._queryCompilationContext.ContextOptions).ParameterizedCollectionMode; + _collectionParameterTranslationMode = RelationalOptionsExtension.Extract(parentVisitor._queryCompilationContext.ContextOptions).ParameterizedCollectionMode; } /// @@ -244,7 +244,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp && methodCallExpression.Arguments[0] is ParameterQueryRootExpression parameterSource && TranslateExpression(methodCallExpression.Arguments[1]) is SqlExpression item && _sqlTranslator.Visit(parameterSource.QueryParameterExpression) is SqlParameterExpression sqlParameterExpression - && !parameterSource.QueryParameterExpression.ShouldNotBeConstantized) + && (parameterSource.QueryParameterExpression.TranslationMode is ParameterTranslationMode.Constant + or null)) { var inExpression = _sqlExpressionFactory.In(item, sqlParameterExpression); var selectExpression = new SelectExpression(inExpression, _sqlAliasManager); @@ -298,26 +299,24 @@ JsonScalarExpression jsonScalar var tableAlias = _sqlAliasManager.GenerateTableAlias(sqlParameterExpression.Name.TrimStart('_')); - var constants = queryParameter.ShouldBeConstantized - || (_parameterizedCollectionMode is ParameterizedCollectionMode.Constants - && !queryParameter.ShouldNotBeConstantized); - var multipleParameters = _parameterizedCollectionMode is ParameterizedCollectionMode.MultipleParameters - && !queryParameter.ShouldNotBeConstantized; - if (constants || multipleParameters) - { - var valuesExpression = new ValuesExpression( - tableAlias, - sqlParameterExpression, - [ValuesOrderingColumnName, ValuesValueColumnName]); - return CreateShapedQueryExpressionForValuesExpression( - valuesExpression, - tableAlias, - parameterQueryRootExpression.ElementType, - sqlParameterExpression.TypeMapping, - sqlParameterExpression.IsNullable); - } - - return TranslatePrimitiveCollection(sqlParameterExpression, property: null, tableAlias); + return (queryParameter.TranslationMode ?? _collectionParameterTranslationMode) switch + { + ParameterTranslationMode.Constant or ParameterTranslationMode.MultipleParameters + => CreateShapedQueryExpressionForValuesExpression( + new ValuesExpression( + tableAlias, + sqlParameterExpression, + [ValuesOrderingColumnName, ValuesValueColumnName]), + tableAlias, + parameterQueryRootExpression.ElementType, + sqlParameterExpression.TypeMapping, + sqlParameterExpression.IsNullable), + + ParameterTranslationMode.Parameter + => TranslatePrimitiveCollection(sqlParameterExpression, property: null, tableAlias), + + _ => throw new UnreachableException() + }; } /// diff --git a/src/efcore/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/efcore/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs index aec6e3cc14d..dbbe1c92358 100644 --- a/src/efcore/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs +++ b/src/efcore/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs @@ -19,7 +19,7 @@ public partial class RelationalShapedQueryCompilingExpressionVisitor : ShapedQue private readonly bool _threadSafetyChecksEnabled; private readonly bool _detailedErrorsEnabled; private readonly bool _useRelationalNulls; - private readonly ParameterizedCollectionMode _parameterizedCollectionMode; + private readonly ParameterTranslationMode _collectionParameterTranslationMode; private readonly bool _isPrecompiling; private readonly RelationalParameterBasedSqlProcessor _relationalParameterBasedSqlProcessor; @@ -55,7 +55,7 @@ public RelationalShapedQueryCompilingExpressionVisitor( _relationalParameterBasedSqlProcessor = relationalDependencies.RelationalParameterBasedSqlProcessorFactory.Create( - new RelationalParameterBasedSqlProcessorParameters(_useRelationalNulls, _parameterizedCollectionMode)); + new RelationalParameterBasedSqlProcessorParameters(_useRelationalNulls, _collectionParameterTranslationMode)); _querySqlGeneratorFactory = relationalDependencies.QuerySqlGeneratorFactory; _contextType = queryCompilationContext.ContextType; @@ -63,7 +63,7 @@ public RelationalShapedQueryCompilingExpressionVisitor( _threadSafetyChecksEnabled = dependencies.CoreSingletonOptions.AreThreadSafetyChecksEnabled; _detailedErrorsEnabled = dependencies.CoreSingletonOptions.AreDetailedErrorsEnabled; _useRelationalNulls = RelationalOptionsExtension.Extract(queryCompilationContext.ContextOptions).UseRelationalNulls; - _parameterizedCollectionMode = RelationalOptionsExtension.Extract(queryCompilationContext.ContextOptions).ParameterizedCollectionMode; + _collectionParameterTranslationMode = RelationalOptionsExtension.Extract(queryCompilationContext.ContextOptions).ParameterizedCollectionMode; _isPrecompiling = queryCompilationContext.IsPrecompiling; } @@ -500,7 +500,7 @@ private Expression CreateRelationalCommandResolverExpression(Expression queryExp RelationalDependencies.RelationalParameterBasedSqlProcessorFactory, queryExpression, _useRelationalNulls, - _parameterizedCollectionMode); + _collectionParameterTranslationMode); var commandLiftableConstant = RelationalDependencies.RelationalLiftableConstantFactory.CreateLiftableConstant( relationalCommandCache, @@ -751,7 +751,7 @@ Expression> Generate _relationalDependenciesRelationalParameterBasedSqlProcessorFactoryProperty), Constant(queryExpression), Constant(_useRelationalNulls), - Constant(_parameterizedCollectionMode, typeof(ParameterizedCollectionMode))), + Constant(_collectionParameterTranslationMode, typeof(ParameterTranslationMode))), contextParameter); } } diff --git a/src/efcore/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/efcore/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index c8f4ec36332..fb89e8c214e 100644 --- a/src/efcore/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/efcore/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -516,7 +516,7 @@ protected override Expression VisitExtension(Expression extensionExpression) name: queryParameter.Name, queryParameter.Type, nullable: false, - queryParameter.ShouldBeConstantized, + queryParameter.TranslationMode, typeMapping: null); } @@ -525,7 +525,7 @@ protected override Expression VisitExtension(Expression extensionExpression) name: queryParameter.Name, queryParameter.Type, queryParameter.Type.IsNullableType(), - queryParameter.ShouldBeConstantized, + queryParameter.TranslationMode, typeMapping: null); case StructuralTypeShaperExpression shaper: diff --git a/src/efcore/src/EFCore.Relational/Query/SqlExpressions/SqlParameterExpression.cs b/src/efcore/src/EFCore.Relational/Query/SqlExpressions/SqlParameterExpression.cs index 48c1602fbaa..a279f21889f 100644 --- a/src/efcore/src/EFCore.Relational/Query/SqlExpressions/SqlParameterExpression.cs +++ b/src/efcore/src/EFCore.Relational/Query/SqlExpressions/SqlParameterExpression.cs @@ -17,7 +17,7 @@ public sealed class SqlParameterExpression : SqlExpression /// The of the expression. /// The associated with the expression. public SqlParameterExpression(string name, Type type, RelationalTypeMapping? typeMapping) - : this(invariantName: name, name: name, type.UnwrapNullableType(), type.IsNullableType(), shouldBeConstantized: false, typeMapping) + : this(invariantName: name, name: name, type.UnwrapNullableType(), type.IsNullableType(), translationMode: null, typeMapping) { } @@ -31,21 +31,21 @@ public SqlParameterExpression(string name, Type type, RelationalTypeMapping? typ /// /// The of the expression. /// Whether this parameter can have null values. - /// Whether the user has indicated that this query parameter should be inlined as a constant. + /// How the parameter should be handled. /// The associated with the expression. public SqlParameterExpression( string invariantName, string name, Type type, bool nullable, - bool shouldBeConstantized, + ParameterTranslationMode? translationMode, RelationalTypeMapping? typeMapping) : base(type.UnwrapNullableType(), typeMapping) { InvariantName = invariantName; Name = name; IsNullable = nullable; - ShouldBeConstantized = shouldBeConstantized; + TranslationMode = translationMode; } /// @@ -65,9 +65,9 @@ public SqlParameterExpression( public bool IsNullable { get; } /// - /// Whether the user has indicated that this query parameter should be inlined as a constant. + /// How the parameter should be handled. /// - public bool ShouldBeConstantized { get; } + public ParameterTranslationMode? TranslationMode { get; } /// /// Applies supplied type mapping to this expression. @@ -75,7 +75,7 @@ public SqlParameterExpression( /// A relational type mapping to apply. /// A new expression which has supplied type mapping. public SqlExpression ApplyTypeMapping(RelationalTypeMapping? typeMapping) - => new SqlParameterExpression(InvariantName, Name, Type, IsNullable, ShouldBeConstantized, typeMapping); + => new SqlParameterExpression(InvariantName, Name, Type, IsNullable, TranslationMode, typeMapping); /// protected override Expression VisitChildren(ExpressionVisitor visitor) diff --git a/src/efcore/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs b/src/efcore/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs index 32af300b2c1..190d56525b8 100644 --- a/src/efcore/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs +++ b/src/efcore/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs @@ -39,7 +39,7 @@ public SqlNullabilityProcessor( { Dependencies = dependencies; UseRelationalNulls = parameters.UseRelationalNulls; - ParameterizedCollectionMode = parameters.ParameterizedCollectionMode; + CollectionParameterTranslationMode = parameters.CollectionParameterTranslationMode; _sqlExpressionFactory = dependencies.SqlExpressionFactory; _nonNullableColumns = []; @@ -61,7 +61,7 @@ public SqlNullabilityProcessor( /// /// A value indicating what translation mode to use. /// - public virtual ParameterizedCollectionMode ParameterizedCollectionMode { get; } + public virtual ParameterTranslationMode CollectionParameterTranslationMode { get; } /// /// Dictionary of current parameter values in use. @@ -126,10 +126,9 @@ protected override Expression VisitExtension(Expression node) var processedValues = new List(); - switch (ParameterizedCollectionMode) + switch (valuesParameter.TranslationMode ?? CollectionParameterTranslationMode) { - case ParameterizedCollectionMode.MultipleParameters - when !valuesParameter.ShouldBeConstantized: + case ParameterTranslationMode.MultipleParameters: { var expandedParameters = _collectionParameterExpansionMap.GetOrAddNew(valuesParameter); for (var i = 0; i < values.Count; i++) @@ -155,11 +154,7 @@ protected override Expression VisitExtension(Expression node) break; } - case ParameterizedCollectionMode.Constants: - case ParameterizedCollectionMode.Parameter - when valuesParameter.ShouldBeConstantized: - case ParameterizedCollectionMode.MultipleParameters - when valuesParameter.ShouldBeConstantized: + case ParameterTranslationMode.Constant: { foreach (var value in values) { @@ -819,18 +814,6 @@ InExpression ProcessInExpressionValues( processedValues = []; - var useParameters = ParameterizedCollectionMode is ParameterizedCollectionMode.MultipleParameters - && !valuesParameter.ShouldBeConstantized; - var useConstants = - ParameterizedCollectionMode is ParameterizedCollectionMode.Constants - || - (ParameterizedCollectionMode is ParameterizedCollectionMode.Parameter - && valuesParameter.ShouldBeConstantized) - || - (ParameterizedCollectionMode is ParameterizedCollectionMode.MultipleParameters - && valuesParameter.ShouldBeConstantized); - var useParameter = ParameterizedCollectionMode is ParameterizedCollectionMode.Parameter - && !valuesParameter.ShouldBeConstantized; var expandedParameters = _collectionParameterExpansionMap.GetOrAddNew(valuesParameter); var expandedParametersCounter = 0; for (var i = 0; i < values.Count; i++) @@ -841,11 +824,11 @@ ParameterizedCollectionMode is ParameterizedCollectionMode.Constants continue; } - switch (useParameters, useConstants, useParameter) + switch (valuesParameter.TranslationMode ?? CollectionParameterTranslationMode) { - case (true, false, false): + case ParameterTranslationMode.MultipleParameters: // see #36311 for more info - case (false, false, true): + case ParameterTranslationMode.Parameter: { // Create parameter for value if we didn't create it yet, // otherwise reuse it. @@ -863,7 +846,7 @@ ParameterizedCollectionMode is ParameterizedCollectionMode.Constants break; } - case (false, true, false): + case ParameterTranslationMode.Constant: { processedValues.Add(_sqlExpressionFactory.Constant(values[i], values[i]?.GetType() ?? typeof(object), sensitive: true, elementTypeMapping)); @@ -1425,7 +1408,7 @@ protected virtual SqlExpression VisitSqlParameter( nullable = false; - if (sqlParameterExpression.ShouldBeConstantized) + if (sqlParameterExpression.TranslationMode is ParameterTranslationMode.Constant) { var parameters = ParametersFacade.GetParametersAndDisableSqlCaching(); diff --git a/src/efcore/src/EFCore.SqlServer/Query/Internal/SqlServerSqlNullabilityProcessor.cs b/src/efcore/src/EFCore.SqlServer/Query/Internal/SqlServerSqlNullabilityProcessor.cs index ea4b96ed806..3215b65578e 100644 --- a/src/efcore/src/EFCore.SqlServer/Query/Internal/SqlServerSqlNullabilityProcessor.cs +++ b/src/efcore/src/EFCore.SqlServer/Query/Internal/SqlServerSqlNullabilityProcessor.cs @@ -187,8 +187,7 @@ protected override Expression VisitExtension(Expression node) switch (node) { case ValuesExpression { ValuesParameter: SqlParameterExpression valuesParameter } valuesExpression - when ParameterizedCollectionMode is ParameterizedCollectionMode.MultipleParameters - && !valuesParameter.ShouldBeConstantized: + when (valuesParameter.TranslationMode ?? CollectionParameterTranslationMode) is ParameterTranslationMode.MultipleParameters: { Check.DebugAssert(valuesParameter.TypeMapping is not null); Check.DebugAssert(valuesParameter.TypeMapping.ElementTypeMapping is not null); @@ -234,8 +233,7 @@ protected override SqlExpression VisitIn(InExpression inExpression, bool allowOp switch (inExpression.ValuesParameter) { case SqlParameterExpression valuesParameter - when ParameterizedCollectionMode is ParameterizedCollectionMode.MultipleParameters - && !valuesParameter.ShouldBeConstantized: + when (valuesParameter.TranslationMode ?? CollectionParameterTranslationMode) is ParameterTranslationMode.MultipleParameters: { Check.DebugAssert(valuesParameter.TypeMapping is not null); Check.DebugAssert(valuesParameter.TypeMapping.ElementTypeMapping is not null); diff --git a/src/efcore/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDatabaseCreator.cs b/src/efcore/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDatabaseCreator.cs index 681a08b37b7..311abf79b3d 100644 --- a/src/efcore/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDatabaseCreator.cs +++ b/src/efcore/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDatabaseCreator.cs @@ -52,7 +52,7 @@ public override void Create() Dependencies.Connection, null, null, - null, + Dependencies.CurrentContext.Context, Dependencies.CommandLogger, CommandSource.Migrations)); Dependencies.Connection.Close(); @@ -101,7 +101,7 @@ public override bool HasTables() Dependencies.Connection, null, null, - null, + Dependencies.CurrentContext.Context, Dependencies.CommandLogger, CommandSource.Migrations))!; return count != 0; diff --git a/src/efcore/src/EFCore.Relational/ParameterizedCollectionMode.cs b/src/efcore/src/EFCore/ParameterTranslationMode.cs similarity index 88% rename from src/efcore/src/EFCore.Relational/ParameterizedCollectionMode.cs rename to src/efcore/src/EFCore/ParameterTranslationMode.cs index 89f48bea90a..964a53688e9 100644 --- a/src/efcore/src/EFCore.Relational/ParameterizedCollectionMode.cs +++ b/src/efcore/src/EFCore/ParameterTranslationMode.cs @@ -6,8 +6,21 @@ namespace Microsoft.EntityFrameworkCore; /// /// Indicates how parameterized collections are translated into SQL. /// -public enum ParameterizedCollectionMode +public enum ParameterTranslationMode { + /// + /// Instructs EF to translate the collection to a set of parameters: + /// WHERE [x].[Id] IN (@ids1, @ids2, @ids3). + /// + /// + /// + /// Note that it's possible to cause EF to translate a specific collection in a specific query to parameter by wrapping the + /// parameterized collection in EF.MultipleParameters: Where(x => EF.MultipleParameters(ids).Contains(x.Id). This overrides + /// the default. + /// + /// + MultipleParameters = 0, + /// /// Instructs EF to translate the collection to a set of constants: /// WHERE [x].[Id] IN (1, 2, 3). @@ -23,7 +36,7 @@ public enum ParameterizedCollectionMode /// the default. /// /// - Constants, + Constant = 1, /// /// Instructs EF to translate the collection to a single array-like parameter: @@ -39,18 +52,5 @@ public enum ParameterizedCollectionMode /// the default. /// /// - Parameter, - - /// - /// Instructs EF to translate the collection to a set of parameters: - /// WHERE [x].[Id] IN (@ids1, @ids2, @ids3). - /// - /// - /// - /// Note that it's possible to cause EF to translate a specific collection in a specific query to parameter by wrapping the - /// parameterized collection in : Where(x => EF.MultipleParameters(ids).Contains(x.Id). This overrides - /// the default. - /// - /// - MultipleParameters, + Parameter = 2, } diff --git a/src/efcore/src/EFCore/Properties/CoreStrings.Designer.cs b/src/efcore/src/EFCore/Properties/CoreStrings.Designer.cs index 2de56528926..e38cb0db359 100644 --- a/src/efcore/src/EFCore/Properties/CoreStrings.Designer.cs +++ b/src/efcore/src/EFCore/Properties/CoreStrings.Designer.cs @@ -358,14 +358,6 @@ public static string CanOnlyConfigureExistingNavigations(object? navigationName, GetString("CanOnlyConfigureExistingNavigations", "0_navigationName", "1_entityType"), navigationName, entityType); - /// - /// The entity type '{entityType}' is configured to use the '{changeTrackingStrategy}' change tracking strategy, but does not implement the required '{notificationInterface}' interface. Implement '{notificationInterface}' on '{entityType}' or use a different change tracking strategy. - /// - public static string ChangeTrackingInterfaceMissing(object? entityType, object? changeTrackingStrategy, object? notificationInterface) - => string.Format( - GetString("ChangeTrackingInterfaceMissing", nameof(entityType), nameof(changeTrackingStrategy), nameof(notificationInterface)), - entityType, changeTrackingStrategy, notificationInterface); - /// /// Unable to save changes because a circular dependency was detected in the data to be saved: '{cycle}'. /// @@ -1125,10 +1117,12 @@ public static string EFConstantNotSupportedInPrecompiledQueries => GetString("EFConstantNotSupportedInPrecompiledQueries"); /// - /// The EF.Constant<T> method may only be used with an argument that can be evaluated client-side and does not contain any reference to database-side entities. + /// The {methodName} method may only be used with an argument that can be evaluated client-side and does not contain any reference to database-side entities. /// - public static string EFConstantWithNonEvaluatableArgument - => GetString("EFConstantWithNonEvaluatableArgument"); + public static string EFMethodWithNonEvaluatableArgument(object? methodName) + => string.Format( + GetString("EFMethodWithNonEvaluatableArgument", nameof(methodName)), + methodName); /// /// The EF.Parameter<T> method may only be used within Entity Framework LINQ queries. @@ -1136,12 +1130,6 @@ public static string EFConstantWithNonEvaluatableArgument public static string EFParameterInvoked => GetString("EFParameterInvoked"); - /// - /// The EF.Parameter<T> method may only be used with an argument that can be evaluated client-side and does not contain any reference to database-side entities. - /// - public static string EFParameterWithNonEvaluatableArgument - => GetString("EFParameterWithNonEvaluatableArgument"); - /// /// Complex type '{complexType}' has no properties defines. Configure at least one property or don't include this type in the model. /// @@ -1452,6 +1440,14 @@ public static string GraphDoesNotContainVertex(object? vertex) public static string HiLoBadBlockSize => GetString("HiLoBadBlockSize"); + /// + /// The entity type '{entityType}' is configured to use the '{changeTrackingStrategy}' change tracking strategy, but does not implement the required '{notificationInterface}' interface. Implement '{notificationInterface}' on '{entityType}' or use a different change tracking strategy. + /// + public static string ChangeTrackingInterfaceMissing(object? entityType, object? changeTrackingStrategy, object? notificationInterface) + => string.Format( + GetString("ChangeTrackingInterfaceMissing", nameof(entityType), nameof(changeTrackingStrategy), nameof(notificationInterface)), + entityType, changeTrackingStrategy, notificationInterface); + /// /// A relationship cycle involving the primary keys of the following entity types was detected: '{entityType}'. This would prevent any entity to be inserted without violating the store constraints. Review the foreign keys defined on the primary keys and either remove or use other properties for at least one of them. /// diff --git a/src/efcore/src/EFCore/Properties/CoreStrings.resx b/src/efcore/src/EFCore/Properties/CoreStrings.resx index 3df16dbe847..ad5fc60c153 100644 --- a/src/efcore/src/EFCore/Properties/CoreStrings.resx +++ b/src/efcore/src/EFCore/Properties/CoreStrings.resx @@ -243,9 +243,6 @@ Navigation '{1_entityType}.{0_navigationName}' was not found. Please add the navigation to the entity type before configuring it. - - The entity type '{entityType}' is configured to use the '{changeTrackingStrategy}' change tracking strategy, but does not implement the required '{notificationInterface}' interface. Implement '{notificationInterface}' on '{entityType}' or use a different change tracking strategy. - Unable to save changes because a circular dependency was detected in the data to be saved: '{cycle}'. @@ -537,15 +534,12 @@ The EF.Constant<T> method is not supported when using precompiled queries. - - The EF.Constant<T> method may only be used with an argument that can be evaluated client-side and does not contain any reference to database-side entities. + + The {methodName} method may only be used with an argument that can be evaluated client-side and does not contain any reference to database-side entities. The EF.Parameter<T> method may only be used within Entity Framework LINQ queries. - - The EF.Parameter<T> method may only be used with an argument that can be evaluated client-side and does not contain any reference to database-side entities. - Complex type '{complexType}' has no properties defines. Configure at least one property or don't include this type in the model. @@ -666,6 +660,9 @@ The block size used for Hi-Lo value generation is not positive. The Hi-Lo generator is usually backed by a SQL sequence and this means that the sequence increment must be positive. + + The entity type '{entityType}' is configured to use the '{changeTrackingStrategy}' change tracking strategy, but does not implement the required '{notificationInterface}' interface. Implement '{notificationInterface}' on '{entityType}' or use a different change tracking strategy. + A relationship cycle involving the primary keys of the following entity types was detected: '{entityType}'. This would prevent any entity to be inserted without violating the store constraints. Review the foreign keys defined on the primary keys and either remove or use other properties for at least one of them. diff --git a/src/efcore/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs b/src/efcore/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs index 44fdec7dbc0..afb319d52c0 100644 --- a/src/efcore/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs +++ b/src/efcore/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs @@ -939,7 +939,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) if (!argumentState.IsEvaluatable) { - throw new InvalidOperationException(CoreStrings.EFConstantWithNonEvaluatableArgument); + throw new InvalidOperationException(CoreStrings.EFMethodWithNonEvaluatableArgument("EF.Constant")); } // Even EF.Constant will be parameter here. @@ -952,21 +952,17 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) case nameof(EF.Parameter): { - var argument = Visit(methodCall.Arguments[0], out var argumentState); - - if (!argumentState.IsEvaluatable) - { - throw new InvalidOperationException(CoreStrings.EFParameterWithNonEvaluatableArgument); - } - - argumentState = argumentState with { StateType = StateType.EvaluatableWithCapturedVariable }; - var evaluatedArgument = ProcessEvaluatableRoot(argument, ref argumentState, forceEvaluation: true); - _state = argumentState; - return Call(method, evaluatedArgument); + return HandleParameter(methodCall, "EF.Parameter"); } } } + // EF.MultipleParameters is defined in Relational, hence the hardcoded values here. + if (method is { Name: "MultipleParameters", DeclaringType.FullName: "Microsoft.EntityFrameworkCore.EFExtensions" }) + { + return HandleParameter(methodCall, "EF.MultipleParameters"); + } + // .NET 10 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans"). // Unfortunately, the LINQ interpreter does not support ref structs, so we rewrite e.g. MemoryExtensions.Contains to // Enumerable.Contains here. See https://github.com/dotnet/runtime/issues/109757,. @@ -1116,6 +1112,21 @@ static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] } return methodCall.Update(@object, ((IReadOnlyList?)arguments) ?? methodCall.Arguments); + + Expression HandleParameter(MethodCallExpression methodCall, string methodName) + { + var argument = Visit(methodCall.Arguments[0], out var argumentState); + + if (!argumentState.IsEvaluatable) + { + throw new InvalidOperationException(CoreStrings.EFMethodWithNonEvaluatableArgument(methodName)); + } + + argumentState = argumentState with { StateType = StateType.EvaluatableWithCapturedVariable }; + var evaluatedArgument = ProcessEvaluatableRoot(argument, ref argumentState, forceEvaluation: true); + _state = argumentState; + return Call(methodCall.Method, evaluatedArgument); + } } /// @@ -1977,8 +1988,7 @@ private static StateType CombineStateTypes(StateType stateType1, StateType state return _parameterizedValues[evaluatableRoot] = new QueryParameterExpression( parameterName, evaluatableRoot.Type, - shouldBeConstantized: false, - shouldNotBeConstantized: false, + translationMode: null, isNonNullableReferenceType); } diff --git a/src/efcore/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs b/src/efcore/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs index 187b0b7263c..80dbb37f040 100644 --- a/src/efcore/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs +++ b/src/efcore/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs @@ -127,20 +127,23 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var queryParameter = (QueryParameterExpression)Visit(methodCallExpression.Arguments[0]); return new QueryParameterExpression( - queryParameter.Name, queryParameter.Type, shouldBeConstantized: true, shouldNotBeConstantized: false, + queryParameter.Name, queryParameter.Type, translationMode: ParameterTranslationMode.Constant, queryParameter.IsNonNullableReferenceType); } case nameof(EF.Parameter): { - var queryParameter = (QueryParameterExpression)Visit(methodCallExpression.Arguments[0]); - return new QueryParameterExpression( - queryParameter.Name, queryParameter.Type, shouldBeConstantized: false, shouldNotBeConstantized: true, - queryParameter.IsNonNullableReferenceType); + return HandleParameter(methodCallExpression, ParameterTranslationMode.Parameter); } } } + // EF.MultipleParameters is defined in Relational, hence the hardcoded values here. + if (method is { Name: "MultipleParameters", DeclaringType.FullName: "Microsoft.EntityFrameworkCore.EFExtensions" }) + { + return HandleParameter(methodCallExpression, ParameterTranslationMode.MultipleParameters); + } + // Normalize list[x] to list.ElementAt(x) if (methodCallExpression is { @@ -236,6 +239,14 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp } return visitedExpression; + + Expression HandleParameter(MethodCallExpression methodCallExpression, ParameterTranslationMode parameterTranslationMode) + { + var queryParameter = (QueryParameterExpression)Visit(methodCallExpression.Arguments[0]); + return new QueryParameterExpression( + queryParameter.Name, queryParameter.Type, parameterTranslationMode, + queryParameter.IsNonNullableReferenceType); + } } private static void VerifyReturnType(Expression expression, ParameterExpression lambdaParameter) diff --git a/src/efcore/src/EFCore/Query/QueryParameterExpression.cs b/src/efcore/src/EFCore/Query/QueryParameterExpression.cs index 696ff1f3b16..dde4c03c09f 100644 --- a/src/efcore/src/EFCore/Query/QueryParameterExpression.cs +++ b/src/efcore/src/EFCore/Query/QueryParameterExpression.cs @@ -14,31 +14,30 @@ namespace Microsoft.EntityFrameworkCore.Query; public class QueryParameterExpression : Expression, IPrintableExpression { /// - /// Creates a new instance of the class with associated query provider. + /// Creates a new instance of the class with associated query provider. /// public QueryParameterExpression(string name, Type type) - : this(name, type, shouldBeConstantized: false, shouldNotBeConstantized: false, isNonNullableReferenceType: false) + : this(name, type, translationMode: null, isNonNullableReferenceType: false) { } /// - /// Creates a new instance of the class with associated query provider. + /// Creates a new instance of the class with associated query provider. /// - public QueryParameterExpression(string name, Type type, bool shouldBeConstantized, bool shouldNotBeConstantized) - : this(name, type, shouldBeConstantized, shouldNotBeConstantized, isNonNullableReferenceType: false) + public QueryParameterExpression(string name, Type type, ParameterTranslationMode translationMode) + : this(name, type, translationMode, isNonNullableReferenceType: false) { } /// - /// Creates a new instance of the class with associated query provider. + /// Creates a new instance of the class with associated query provider. /// [Experimental(EFDiagnostics.PrecompiledQueryExperimental)] - public QueryParameterExpression(string name, Type type, bool shouldBeConstantized, bool shouldNotBeConstantized, bool isNonNullableReferenceType) + public QueryParameterExpression(string name, Type type, ParameterTranslationMode? translationMode, bool isNonNullableReferenceType) { Name = name; Type = type; - ShouldBeConstantized = shouldBeConstantized; - ShouldNotBeConstantized = shouldNotBeConstantized; + TranslationMode = translationMode; IsNonNullableReferenceType = isNonNullableReferenceType; } @@ -61,14 +60,9 @@ public QueryParameterExpression(string name, Type type, bool shouldBeConstantize public virtual bool IsNonNullableReferenceType { get; } /// - /// Whether the user has indicated that this query parameter should be inlined as a constant. + /// How should the parameter be handled. /// - public virtual bool ShouldBeConstantized { get; } - - /// - /// Whether the user has indicated that this query parameter shouldn't be inlined as a constant. - /// - public virtual bool ShouldNotBeConstantized { get; } + public virtual ParameterTranslationMode? TranslationMode { get; } /// public override ExpressionType NodeType @@ -92,11 +86,10 @@ public override bool Equals(object? obj) private bool Equals(QueryParameterExpression queryParameterExpression) => Name == queryParameterExpression.Name && Type == queryParameterExpression.Type - && ShouldBeConstantized == queryParameterExpression.ShouldBeConstantized - && ShouldNotBeConstantized == queryParameterExpression.ShouldNotBeConstantized + && TranslationMode == queryParameterExpression.TranslationMode && IsNonNullableReferenceType == queryParameterExpression.IsNonNullableReferenceType; /// public override int GetHashCode() - => HashCode.Combine(Name, Type, ShouldBeConstantized, ShouldNotBeConstantized, IsNonNullableReferenceType); + => HashCode.Combine(Name, Type, TranslationMode, IsNonNullableReferenceType); } diff --git a/src/efcore/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs b/src/efcore/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs index 1cd086ec586..97180c4cf39 100644 --- a/src/efcore/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs +++ b/src/efcore/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs @@ -2338,8 +2338,8 @@ public override async Task EF_Constant_does_not_parameterized_as_part_of_bigger_ public override async Task EF_Constant_with_non_evaluatable_argument_throws(bool async) { await base.EF_Constant_with_non_evaluatable_argument_throws(async); - AssertSql( - ); + + AssertSql(); } public override Task EF_Parameter(bool async) diff --git a/src/efcore/test/EFCore.Relational.Specification.Tests/Query/AdHocMiscellaneousQueryRelationalTestBase.cs b/src/efcore/test/EFCore.Relational.Specification.Tests/Query/AdHocMiscellaneousQueryRelationalTestBase.cs index 2496b692e1d..a572b1d9090 100644 --- a/src/efcore/test/EFCore.Relational.Specification.Tests/Query/AdHocMiscellaneousQueryRelationalTestBase.cs +++ b/src/efcore/test/EFCore.Relational.Specification.Tests/Query/AdHocMiscellaneousQueryRelationalTestBase.cs @@ -20,6 +20,8 @@ protected void ClearLog() protected void AssertSql(params string[] expected) => TestSqlLoggerFactory.AssertBaseline(expected); + protected abstract DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterTranslationMode parameterizedCollectionMode); + #region 2951 [ConditionalFact] @@ -268,8 +270,6 @@ public class Entity #region Inlined redacting - protected abstract DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterizedCollectionMode parameterizedCollectionMode); - [ConditionalTheory] [MemberData(nameof(InlinedRedactingData))] public virtual async Task Check_inlined_constants_redacting(bool async, bool enableSensitiveDataLogging) @@ -277,7 +277,7 @@ public virtual async Task Check_inlined_constants_redacting(bool async, bool ena var contextFactory = await InitializeAsync( onConfiguring: o => { - SetParameterizedCollectionMode(o, ParameterizedCollectionMode.Constants); + SetParameterizedCollectionMode(o, ParameterTranslationMode.Constant); o.EnableSensitiveDataLogging(enableSensitiveDataLogging); }); using var context = contextFactory.CreateContext(); @@ -324,7 +324,7 @@ public class TestEntity public async Task Entity_equality_with_Contains_and_Parameter(bool async) { var contextFactory = await InitializeAsync( - onConfiguring: o => SetParameterizedCollectionMode(o, ParameterizedCollectionMode.Parameter)); + onConfiguring: o => SetParameterizedCollectionMode(o, ParameterTranslationMode.Parameter)); using var context = contextFactory.CreateContext(); List details = [new Context36311.BlogDetails { Id = 1 }, new Context36311.BlogDetails { Id = 2 }]; diff --git a/src/efcore/test/EFCore.Relational.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryRelationalTestBase.cs b/src/efcore/test/EFCore.Relational.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryRelationalTestBase.cs index 2cc15f6fafd..e5132067c77 100644 --- a/src/efcore/test/EFCore.Relational.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryRelationalTestBase.cs +++ b/src/efcore/test/EFCore.Relational.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryRelationalTestBase.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Linq; + namespace Microsoft.EntityFrameworkCore.Query; #nullable disable @@ -12,7 +14,7 @@ public abstract class NonSharedPrimitiveCollectionsQueryRelationalTestBase(NonSh public override Task Array_of_byte() => AssertTranslationFailed(() => TestArray((byte)1, (byte)2)); - protected abstract DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterizedCollectionMode parameterizedCollectionMode); + protected abstract DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterTranslationMode parameterizedCollectionMode); [ConditionalFact] public virtual async Task Column_collection_inside_json_owned_entity() @@ -36,11 +38,15 @@ public virtual async Task Column_collection_inside_json_owned_entity() Assert.Equivalent(new[] { "foo", "bar" }, result.Owned.Strings); } - [ConditionalFact] - public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_constants() + protected static IEnumerable ParameterTranslationModeValues() + => Enum.GetValues().Select(x => [x]); + + [ConditionalTheory] + [MemberData(nameof(ParameterTranslationModeValues))] + public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_mode(ParameterTranslationMode mode) { var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.Constants), + onConfiguring: b => SetParameterizedCollectionMode(b, mode), seed: context => { context.AddRange( @@ -56,11 +62,12 @@ public virtual async Task Parameter_collection_Count_with_column_predicate_with_ Assert.Equivalent(new[] { 100 }, result); } - [ConditionalFact] - public virtual async Task Parameter_collection_Contains_with_default_constants() + [ConditionalTheory] + [MemberData(nameof(ParameterTranslationModeValues))] + public virtual async Task Parameter_collection_Contains_with_default_mode(ParameterTranslationMode mode) { var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.Constants), + onConfiguring: b => SetParameterizedCollectionMode(b, mode), seed: context => { context.AddRange( @@ -77,53 +84,12 @@ public virtual async Task Parameter_collection_Contains_with_default_constants() Assert.Equivalent(new[] { 2 }, result); } - [ConditionalFact] - public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_constants_EF_Parameter() - { - var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.Constants), - seed: context => - { - context.AddRange( - new TestEntity { Id = 1 }, - new TestEntity { Id = 100 }); - return context.SaveChangesAsync(); - }); - - await using var context = contextFactory.CreateContext(); - - var ids = new[] { 2, 999 }; - var result = await context.Set().Where(c => EF.Parameter(ids).Count(i => i > c.Id) == 1).Select(x => x.Id) - .ToListAsync(); - Assert.Equivalent(new[] { 100 }, result); - } - - [ConditionalFact] - public virtual async Task Parameter_collection_Contains_with_default_constants_EF_Parameter() - { - var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.Constants), - seed: context => - { - context.AddRange( - new TestEntity { Id = 1 }, - new TestEntity { Id = 2 }, - new TestEntity { Id = 100 }); - return context.SaveChangesAsync(); - }); - - await using var context = contextFactory.CreateContext(); - - var ints = new[] { 2, 999 }; - var result = await context.Set().Where(c => EF.Parameter(ints).Contains(c.Id)).Select(x => x.Id).ToListAsync(); - Assert.Equivalent(new[] { 2 }, result); - } - - [ConditionalFact] - public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_parameter() + [ConditionalTheory] + [MemberData(nameof(ParameterTranslationModeValues))] + public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Constant(ParameterTranslationMode mode) { var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.Parameter), + onConfiguring: b => SetParameterizedCollectionMode(b, mode), seed: context => { context.AddRange( @@ -135,15 +101,16 @@ public virtual async Task Parameter_collection_Count_with_column_predicate_with_ await using var context = contextFactory.CreateContext(); var ids = new[] { 2, 999 }; - var result = await context.Set().Where(c => ids.Count(i => i > c.Id) == 1).Select(x => x.Id).ToListAsync(); + var result = await context.Set().Where(c => EF.Constant(ids).Count(i => i > c.Id) == 1).Select(x => x.Id).ToListAsync(); Assert.Equivalent(new[] { 100 }, result); } - [ConditionalFact] - public virtual async Task Parameter_collection_Contains_with_default_parameter() + [ConditionalTheory] + [MemberData(nameof(ParameterTranslationModeValues))] + public virtual async Task Parameter_collection_Contains_with_default_mode_EF_Constant(ParameterTranslationMode mode) { var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.Parameter), + onConfiguring: b => SetParameterizedCollectionMode(b, mode), seed: context => { context.AddRange( @@ -156,15 +123,16 @@ public virtual async Task Parameter_collection_Contains_with_default_parameter() await using var context = contextFactory.CreateContext(); var ints = new[] { 2, 999 }; - var result = await context.Set().Where(c => ints.Contains(c.Id)).Select(x => x.Id).ToListAsync(); + var result = await context.Set().Where(c => EF.Constant(ints).Contains(c.Id)).Select(x => x.Id).ToListAsync(); Assert.Equivalent(new[] { 2 }, result); } - [ConditionalFact] - public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_parameter_EF_Constant() + [ConditionalTheory] + [MemberData(nameof(ParameterTranslationModeValues))] + public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Parameter(ParameterTranslationMode mode) { var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.Parameter), + onConfiguring: b => SetParameterizedCollectionMode(b, mode), seed: context => { context.AddRange( @@ -176,15 +144,17 @@ public virtual async Task Parameter_collection_Count_with_column_predicate_with_ await using var context = contextFactory.CreateContext(); var ids = new[] { 2, 999 }; - var result = await context.Set().Where(c => EF.Constant(ids).Count(i => i > c.Id) == 1).Select(x => x.Id).ToListAsync(); + var result = await context.Set().Where(c => EF.Parameter(ids).Count(i => i > c.Id) == 1).Select(x => x.Id) + .ToListAsync(); Assert.Equivalent(new[] { 100 }, result); } - [ConditionalFact] - public virtual async Task Parameter_collection_Contains_with_default_parameter_EF_Constant() + [ConditionalTheory] + [MemberData(nameof(ParameterTranslationModeValues))] + public virtual async Task Parameter_collection_Contains_with_default_mode_EF_Parameter(ParameterTranslationMode mode) { var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.Parameter), + onConfiguring: b => SetParameterizedCollectionMode(b, mode), seed: context => { context.AddRange( @@ -197,15 +167,16 @@ public virtual async Task Parameter_collection_Contains_with_default_parameter_E await using var context = contextFactory.CreateContext(); var ints = new[] { 2, 999 }; - var result = await context.Set().Where(c => EF.Constant(ints).Contains(c.Id)).Select(x => x.Id).ToListAsync(); + var result = await context.Set().Where(c => EF.Parameter(ints).Contains(c.Id)).Select(x => x.Id).ToListAsync(); Assert.Equivalent(new[] { 2 }, result); } - [ConditionalFact] - public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_multiple_parameters() + [ConditionalTheory] + [MemberData(nameof(ParameterTranslationModeValues))] + public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_mode_EF_MultipleParameters(ParameterTranslationMode mode) { var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.MultipleParameters), + onConfiguring: b => SetParameterizedCollectionMode(b, mode), seed: context => { context.AddRange( @@ -217,15 +188,17 @@ public virtual async Task Parameter_collection_Count_with_column_predicate_with_ await using var context = contextFactory.CreateContext(); var ids = new[] { 2, 999 }; - var result = await context.Set().Where(c => ids.Count(i => i > c.Id) == 1).Select(x => x.Id).ToListAsync(); + var result = await context.Set().Where(c => EF.MultipleParameters(ids).Count(i => i > c.Id) == 1).Select(x => x.Id) + .ToListAsync(); Assert.Equivalent(new[] { 100 }, result); } - [ConditionalFact] - public virtual async Task Parameter_collection_Contains_with_default_multiple_parameters() + [ConditionalTheory] + [MemberData(nameof(ParameterTranslationModeValues))] + public virtual async Task Parameter_collection_Contains_with_default_mode_EF_MultipleParameters(ParameterTranslationMode mode) { var contextFactory = await InitializeAsync( - onConfiguring: b => SetParameterizedCollectionMode(b, ParameterizedCollectionMode.MultipleParameters), + onConfiguring: b => SetParameterizedCollectionMode(b, mode), seed: context => { context.AddRange( @@ -238,7 +211,7 @@ public virtual async Task Parameter_collection_Contains_with_default_multiple_pa await using var context = contextFactory.CreateContext(); var ints = new[] { 2, 999 }; - var result = await context.Set().Where(c => ints.Contains(c.Id)).Select(x => x.Id).ToListAsync(); + var result = await context.Set().Where(c => EF.MultipleParameters(ints).Contains(c.Id)).Select(x => x.Id).ToListAsync(); Assert.Equivalent(new[] { 2 }, result); } diff --git a/src/efcore/test/EFCore.Relational.Specification.Tests/Query/NorthwindWhereQueryRelationalTestBase.cs b/src/efcore/test/EFCore.Relational.Specification.Tests/Query/NorthwindWhereQueryRelationalTestBase.cs index 819f1a58c67..26b62b45334 100644 --- a/src/efcore/test/EFCore.Relational.Specification.Tests/Query/NorthwindWhereQueryRelationalTestBase.cs +++ b/src/efcore/test/EFCore.Relational.Specification.Tests/Query/NorthwindWhereQueryRelationalTestBase.cs @@ -1,6 +1,8 @@ // 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.TestModels.Northwind; + namespace Microsoft.EntityFrameworkCore.Query; #nullable disable @@ -11,6 +13,18 @@ public abstract class NorthwindWhereQueryRelationalTestBase(TFixture f public override Task Where_bool_client_side_negated(bool async) => AssertTranslationFailed(() => base.Where_bool_client_side_negated(async)); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task EF_MultipleParameters_with_non_evaluatable_argument_throws(bool async) + { + var exception = await Assert.ThrowsAsync( + () => AssertQuery( + async, + ss => ss.Set().Where(c => c.Orders == EF.MultipleParameters(c.Orders)))); + + Assert.Equal(CoreStrings.EFMethodWithNonEvaluatableArgument("EF.MultipleParameters"), exception.Message); + } + protected override QueryAsserter CreateQueryAsserter(TFixture fixture) => new RelationalQueryAsserter( fixture, RewriteExpectedQueryExpression, RewriteServerQueryExpression); diff --git a/src/efcore/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs b/src/efcore/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs index 76b0ad64190..a3256aa31b7 100644 --- a/src/efcore/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs +++ b/src/efcore/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs @@ -2082,7 +2082,7 @@ public virtual async Task EF_Constant_with_non_evaluatable_argument_throws(bool async, ss => ss.Set().Where(c => c.CustomerID == EF.Constant(c.CustomerID)))); - Assert.Equal(CoreStrings.EFConstantWithNonEvaluatableArgument, exception.Message); + Assert.Equal(CoreStrings.EFMethodWithNonEvaluatableArgument("EF.Constant"), exception.Message); } [ConditionalTheory] @@ -2129,7 +2129,7 @@ public virtual async Task EF_Parameter_with_non_evaluatable_argument_throws(bool async, ss => ss.Set().Where(c => c.CustomerID == EF.Parameter(c.CustomerID)))); - Assert.Equal(CoreStrings.EFParameterWithNonEvaluatableArgument, exception.Message); + Assert.Equal(CoreStrings.EFMethodWithNonEvaluatableArgument("EF.Parameter"), exception.Message); } private class EntityWithImplicitCast(int value) diff --git a/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/AdHocMiscellaneousQuerySqlServerTest.cs b/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/AdHocMiscellaneousQuerySqlServerTest.cs index cc0a312389c..d017fa4209d 100644 --- a/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/AdHocMiscellaneousQuerySqlServerTest.cs +++ b/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/AdHocMiscellaneousQuerySqlServerTest.cs @@ -19,7 +19,7 @@ public class AdHocMiscellaneousQuerySqlServerTest(NonSharedFixture fixture) : Ad protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; - protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterizedCollectionMode parameterizedCollectionMode) + protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterTranslationMode parameterizedCollectionMode) { new SqlServerDbContextOptionsBuilder(optionsBuilder).UseParameterizedCollectionMode(parameterizedCollectionMode); diff --git a/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs b/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs index 3016f7c7b93..b7b86e420bc 100644 --- a/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs +++ b/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs @@ -10,7 +10,7 @@ namespace Microsoft.EntityFrameworkCore.Query; public class NonSharedPrimitiveCollectionsQuerySqlServerTest(NonSharedFixture fixture) : NonSharedPrimitiveCollectionsQueryRelationalTestBase(fixture) { - protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterizedCollectionMode parameterizedCollectionMode) + protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterTranslationMode parameterizedCollectionMode) { new SqlServerDbContextOptionsBuilder(optionsBuilder).UseParameterizedCollectionMode(parameterizedCollectionMode); @@ -785,12 +785,16 @@ FROM [TestEntityWithOwned] AS [t] """); } - public override async Task Parameter_collection_Count_with_column_predicate_with_default_constants() + public override async Task Parameter_collection_Count_with_column_predicate_with_default_mode(ParameterTranslationMode mode) { - await base.Parameter_collection_Count_with_column_predicate_with_default_constants(); + await base.Parameter_collection_Count_with_column_predicate_with_default_mode(mode); - AssertSql( - """ + switch (mode) + { + case ParameterTranslationMode.Constant: + { + AssertSql( + """ SELECT [t].[Id] FROM [TestEntity] AS [t] WHERE ( @@ -798,43 +802,68 @@ SELECT COUNT(*) FROM (VALUES (2), (999)) AS [i]([Value]) WHERE [i].[Value] > [t].[Id]) = 1 """); - } + break; + } - public override async Task Parameter_collection_Contains_with_default_constants() - { - await base.Parameter_collection_Contains_with_default_constants(); + case ParameterTranslationMode.Parameter: + { + AssertSql( + """ +@ids='[2,999]' (Size = 4000) - AssertSql( - """ SELECT [t].[Id] FROM [TestEntity] AS [t] -WHERE [t].[Id] IN (2, 999) +WHERE ( + SELECT COUNT(*) + FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] + WHERE [i].[value] > [t].[Id]) = 1 """); - } - - public override async Task Parameter_collection_Count_with_column_predicate_with_default_constants_EF_Parameter() - { - await base.Parameter_collection_Count_with_column_predicate_with_default_constants_EF_Parameter(); + break; + } - AssertSql( - """ -@ids='[2,999]' (Size = 4000) + case ParameterTranslationMode.MultipleParameters: + { + AssertSql( + """ +@ids1='2' +@ids2='999' SELECT [t].[Id] FROM [TestEntity] AS [t] WHERE ( SELECT COUNT(*) - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] - WHERE [i].[value] > [t].[Id]) = 1 + FROM (VALUES (@ids1), (@ids2)) AS [i]([Value]) + WHERE [i].[Value] > [t].[Id]) = 1 """); + break; + } + + default: + throw new NotImplementedException(); + } } - public override async Task Parameter_collection_Contains_with_default_constants_EF_Parameter() + public override async Task Parameter_collection_Contains_with_default_mode(ParameterTranslationMode mode) { - await base.Parameter_collection_Contains_with_default_constants_EF_Parameter(); + await base.Parameter_collection_Contains_with_default_mode(mode); - AssertSql( - """ + switch (mode) + { + case ParameterTranslationMode.Constant: + { + AssertSql( + """ +SELECT [t].[Id] +FROM [TestEntity] AS [t] +WHERE [t].[Id] IN (2, 999) +"""); + break; + } + + case ParameterTranslationMode.Parameter: + { + AssertSql( + """ @ints='[2,999]' (Size = 4000) SELECT [t].[Id] @@ -844,72 +873,92 @@ SELECT [i].[value] FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] ) """); + break; + } + + case ParameterTranslationMode.MultipleParameters: + { + AssertSql( + """ +@ints1='2' +@ints2='999' + +SELECT [t].[Id] +FROM [TestEntity] AS [t] +WHERE [t].[Id] IN (@ints1, @ints2) +"""); + break; + } + + default: + throw new NotImplementedException(); + } } - public override async Task Parameter_collection_Count_with_column_predicate_with_default_parameter() + public override async Task Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Constant(ParameterTranslationMode mode) { - await base.Parameter_collection_Count_with_column_predicate_with_default_parameter(); + await base.Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Constant(mode); AssertSql( """ -@ids='[2,999]' (Size = 4000) - SELECT [t].[Id] FROM [TestEntity] AS [t] WHERE ( SELECT COUNT(*) - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] - WHERE [i].[value] > [t].[Id]) = 1 + FROM (VALUES (2), (999)) AS [i]([Value]) + WHERE [i].[Value] > [t].[Id]) = 1 """); } - public override async Task Parameter_collection_Contains_with_default_parameter() + public override async Task Parameter_collection_Contains_with_default_mode_EF_Constant(ParameterTranslationMode mode) { - await base.Parameter_collection_Contains_with_default_parameter(); + await base.Parameter_collection_Contains_with_default_mode_EF_Constant(mode); AssertSql( """ -@ints='[2,999]' (Size = 4000) - SELECT [t].[Id] FROM [TestEntity] AS [t] -WHERE [t].[Id] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [t].[Id] IN (2, 999) """); } - public override async Task Parameter_collection_Count_with_column_predicate_with_default_parameter_EF_Constant() + public override async Task Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Parameter(ParameterTranslationMode mode) { - await base.Parameter_collection_Count_with_column_predicate_with_default_parameter_EF_Constant(); + await base.Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Parameter(mode); AssertSql( """ +@ids='[2,999]' (Size = 4000) + SELECT [t].[Id] FROM [TestEntity] AS [t] WHERE ( SELECT COUNT(*) - FROM (VALUES (2), (999)) AS [i]([Value]) - WHERE [i].[Value] > [t].[Id]) = 1 + FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] + WHERE [i].[value] > [t].[Id]) = 1 """); } - public override async Task Parameter_collection_Contains_with_default_parameter_EF_Constant() + public override async Task Parameter_collection_Contains_with_default_mode_EF_Parameter(ParameterTranslationMode mode) { - await base.Parameter_collection_Contains_with_default_parameter_EF_Constant(); + await base.Parameter_collection_Contains_with_default_mode_EF_Parameter(mode); AssertSql( """ +@ints='[2,999]' (Size = 4000) + SELECT [t].[Id] FROM [TestEntity] AS [t] -WHERE [t].[Id] IN (2, 999) +WHERE [t].[Id] IN ( + SELECT [i].[value] + FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] +) """); } - public override async Task Parameter_collection_Count_with_column_predicate_with_default_multiple_parameters() + public override async Task Parameter_collection_Count_with_column_predicate_with_default_mode_EF_MultipleParameters(ParameterTranslationMode mode) { - await base.Parameter_collection_Count_with_column_predicate_with_default_multiple_parameters(); + await base.Parameter_collection_Count_with_column_predicate_with_default_mode_EF_MultipleParameters(mode); AssertSql( """ @@ -925,9 +974,9 @@ SELECT COUNT(*) """); } - public override async Task Parameter_collection_Contains_with_default_multiple_parameters() + public override async Task Parameter_collection_Contains_with_default_mode_EF_MultipleParameters(ParameterTranslationMode mode) { - await base.Parameter_collection_Contains_with_default_multiple_parameters(); + await base.Parameter_collection_Contains_with_default_mode_EF_MultipleParameters(mode); AssertSql( """ diff --git a/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs b/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs index f0f1f20ff23..bb81e7cd85e 100644 --- a/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs +++ b/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs @@ -2970,6 +2970,13 @@ FROM [Orders] AS [o] """); } + public override async Task EF_MultipleParameters_with_non_evaluatable_argument_throws(bool async) + { + await base.EF_MultipleParameters_with_non_evaluatable_argument_throws(async); + + AssertSql(); + } + #region Evaluation order of operators public override async Task Take_and_Where_evaluation_order(bool async) diff --git a/src/efcore/test/EFCore.Sqlite.FunctionalTests/Query/AdHocMiscellaneousQuerySqliteTest.cs b/src/efcore/test/EFCore.Sqlite.FunctionalTests/Query/AdHocMiscellaneousQuerySqliteTest.cs index e878daf1ff8..f80084d99e5 100644 --- a/src/efcore/test/EFCore.Sqlite.FunctionalTests/Query/AdHocMiscellaneousQuerySqliteTest.cs +++ b/src/efcore/test/EFCore.Sqlite.FunctionalTests/Query/AdHocMiscellaneousQuerySqliteTest.cs @@ -10,7 +10,7 @@ public class AdHocMiscellaneousQuerySqliteTest(NonSharedFixture fixture) : AdHoc protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance; - protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterizedCollectionMode parameterizedCollectionMode) + protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterTranslationMode parameterizedCollectionMode) { new SqliteDbContextOptionsBuilder(optionsBuilder).UseParameterizedCollectionMode(parameterizedCollectionMode); diff --git a/src/efcore/test/EFCore.Sqlite.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqliteTest.cs b/src/efcore/test/EFCore.Sqlite.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqliteTest.cs index fbddf89fa4f..2f0eca42149 100644 --- a/src/efcore/test/EFCore.Sqlite.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqliteTest.cs +++ b/src/efcore/test/EFCore.Sqlite.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqliteTest.cs @@ -7,7 +7,7 @@ namespace Microsoft.EntityFrameworkCore.Query; public class NonSharedPrimitiveCollectionsQuerySqliteTest(NonSharedFixture fixture) : NonSharedPrimitiveCollectionsQueryRelationalTestBase(fixture) { - protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterizedCollectionMode parameterizedCollectionMode) + protected override DbContextOptionsBuilder SetParameterizedCollectionMode(DbContextOptionsBuilder optionsBuilder, ParameterTranslationMode parameterizedCollectionMode) { new SqliteDbContextOptionsBuilder(optionsBuilder).UseParameterizedCollectionMode(parameterizedCollectionMode); @@ -329,12 +329,16 @@ LIMIT 2 """); } - public override async Task Parameter_collection_Count_with_column_predicate_with_default_constants() + public override async Task Parameter_collection_Count_with_column_predicate_with_default_mode(ParameterTranslationMode mode) { - await base.Parameter_collection_Count_with_column_predicate_with_default_constants(); - - AssertSql( - """ + await base.Parameter_collection_Count_with_column_predicate_with_default_mode(mode); + + switch (mode) + { + case ParameterTranslationMode.Constant: + { + AssertSql( + """ SELECT "t"."Id" FROM "TestEntity" AS "t" WHERE ( @@ -342,43 +346,68 @@ SELECT COUNT(*) FROM (SELECT 2 AS "Value" UNION ALL VALUES (999)) AS "i" WHERE "i"."Value" > "t"."Id") = 1 """); - } + break; + } - public override async Task Parameter_collection_Contains_with_default_constants() - { - await base.Parameter_collection_Contains_with_default_constants(); + case ParameterTranslationMode.Parameter: + { + AssertSql( + """ +@ids='[2,999]' (Size = 7) - AssertSql( - """ SELECT "t"."Id" FROM "TestEntity" AS "t" -WHERE "t"."Id" IN (2, 999) +WHERE ( + SELECT COUNT(*) + FROM json_each(@ids) AS "i" + WHERE "i"."value" > "t"."Id") = 1 """); - } - - public override async Task Parameter_collection_Count_with_column_predicate_with_default_constants_EF_Parameter() - { - await base.Parameter_collection_Count_with_column_predicate_with_default_constants_EF_Parameter(); + break; + } - AssertSql( - """ -@ids='[2,999]' (Size = 7) + case ParameterTranslationMode.MultipleParameters: + { + AssertSql( + """ +@ids1='2' +@ids2='999' SELECT "t"."Id" FROM "TestEntity" AS "t" WHERE ( SELECT COUNT(*) - FROM json_each(@ids) AS "i" - WHERE "i"."value" > "t"."Id") = 1 + FROM (SELECT @ids1 AS "Value" UNION ALL VALUES (@ids2)) AS "i" + WHERE "i"."Value" > "t"."Id") = 1 """); + break; + } + + default: + throw new NotImplementedException(); + } } - public override async Task Parameter_collection_Contains_with_default_constants_EF_Parameter() + public override async Task Parameter_collection_Contains_with_default_mode(ParameterTranslationMode mode) { - await base.Parameter_collection_Contains_with_default_constants_EF_Parameter(); + await base.Parameter_collection_Contains_with_default_mode(mode); + + switch (mode) + { + case ParameterTranslationMode.Constant: + { + AssertSql( + """ +SELECT "t"."Id" +FROM "TestEntity" AS "t" +WHERE "t"."Id" IN (2, 999) +"""); + break; + } - AssertSql( - """ + case ParameterTranslationMode.Parameter: + { + AssertSql( + """ @ints='[2,999]' (Size = 7) SELECT "t"."Id" @@ -388,72 +417,92 @@ public override async Task Parameter_collection_Contains_with_default_constants_ FROM json_each(@ints) AS "i" ) """); + break; + } + + case ParameterTranslationMode.MultipleParameters: + { + AssertSql( + """ +@ints1='2' +@ints2='999' + +SELECT "t"."Id" +FROM "TestEntity" AS "t" +WHERE "t"."Id" IN (@ints1, @ints2) +"""); + break; + } + + default: + throw new NotImplementedException(); + } } - public override async Task Parameter_collection_Count_with_column_predicate_with_default_parameter() + public override async Task Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Constant(ParameterTranslationMode mode) { - await base.Parameter_collection_Count_with_column_predicate_with_default_parameter(); + await base.Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Constant(mode); AssertSql( """ -@ids='[2,999]' (Size = 7) - SELECT "t"."Id" FROM "TestEntity" AS "t" WHERE ( SELECT COUNT(*) - FROM json_each(@ids) AS "i" - WHERE "i"."value" > "t"."Id") = 1 + FROM (SELECT 2 AS "Value" UNION ALL VALUES (999)) AS "i" + WHERE "i"."Value" > "t"."Id") = 1 """); } - public override async Task Parameter_collection_Contains_with_default_parameter() + public override async Task Parameter_collection_Contains_with_default_mode_EF_Constant(ParameterTranslationMode mode) { - await base.Parameter_collection_Contains_with_default_parameter(); + await base.Parameter_collection_Contains_with_default_mode_EF_Constant(mode); AssertSql( """ -@ints='[2,999]' (Size = 7) - SELECT "t"."Id" FROM "TestEntity" AS "t" -WHERE "t"."Id" IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" -) +WHERE "t"."Id" IN (2, 999) """); } - public override async Task Parameter_collection_Count_with_column_predicate_with_default_parameter_EF_Constant() + public override async Task Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Parameter(ParameterTranslationMode mode) { - await base.Parameter_collection_Count_with_column_predicate_with_default_parameter_EF_Constant(); + await base.Parameter_collection_Count_with_column_predicate_with_default_mode_EF_Parameter(mode); AssertSql( """ +@ids='[2,999]' (Size = 7) + SELECT "t"."Id" FROM "TestEntity" AS "t" WHERE ( SELECT COUNT(*) - FROM (SELECT 2 AS "Value" UNION ALL VALUES (999)) AS "i" - WHERE "i"."Value" > "t"."Id") = 1 + FROM json_each(@ids) AS "i" + WHERE "i"."value" > "t"."Id") = 1 """); } - public override async Task Parameter_collection_Contains_with_default_parameter_EF_Constant() + public override async Task Parameter_collection_Contains_with_default_mode_EF_Parameter(ParameterTranslationMode mode) { - await base.Parameter_collection_Contains_with_default_parameter_EF_Constant(); + await base.Parameter_collection_Contains_with_default_mode_EF_Parameter(mode); AssertSql( """ +@ints='[2,999]' (Size = 7) + SELECT "t"."Id" FROM "TestEntity" AS "t" -WHERE "t"."Id" IN (2, 999) +WHERE "t"."Id" IN ( + SELECT "i"."value" + FROM json_each(@ints) AS "i" +) """); } - public override async Task Parameter_collection_Count_with_column_predicate_with_default_multiple_parameters() + public override async Task Parameter_collection_Count_with_column_predicate_with_default_mode_EF_MultipleParameters(ParameterTranslationMode mode) { - await base.Parameter_collection_Count_with_column_predicate_with_default_multiple_parameters(); + await base.Parameter_collection_Count_with_column_predicate_with_default_mode_EF_MultipleParameters(mode); AssertSql( """ @@ -469,9 +518,9 @@ SELECT COUNT(*) """); } - public override async Task Parameter_collection_Contains_with_default_multiple_parameters() + public override async Task Parameter_collection_Contains_with_default_mode_EF_MultipleParameters(ParameterTranslationMode mode) { - await base.Parameter_collection_Contains_with_default_multiple_parameters(); + await base.Parameter_collection_Contains_with_default_mode_EF_MultipleParameters(mode); AssertSql( """ diff --git a/src/source-manifest.json b/src/source-manifest.json index d3d733969c8..7055c1dbb95 100644 --- a/src/source-manifest.json +++ b/src/source-manifest.json @@ -7,10 +7,10 @@ "commitSha": "4e526204e83e615efe8eb5743be7fbccfa4e492a" }, { - "barId": 275017, + "barId": 275303, "path": "aspnetcore", "remoteUri": "https://github.com/dotnet/aspnetcore", - "commitSha": "f905bebf6206d90192c66e84993963f2f8a506c4" + "commitSha": "9d4270cd93259b2ad6740bd8bb8315615ce93c94" }, { "barId": 273742, @@ -19,10 +19,10 @@ "commitSha": "95da703c5b2c85bf4c81b92c1ea1994c7de4970f" }, { - "barId": 274651, + "barId": 275338, "path": "command-line-api", "remoteUri": "https://github.com/dotnet/command-line-api", - "commitSha": "12473cd29fac1b9070c71ed8f5a43cada6872905" + "commitSha": "dbc3781d3398b69cd9ffe24f60a7b77110bdadbf" }, { "barId": 274234, @@ -31,16 +31,16 @@ "commitSha": "227da60247996ce95c272cdb5941163c669224e8" }, { - "barId": 275020, + "barId": 275333, "path": "diagnostics", "remoteUri": "https://github.com/dotnet/diagnostics", - "commitSha": "c3c57bbd7ce6950087b2e99be7a1a1170cf94923" + "commitSha": "15863918a40fc31d0498ca5f1b8a5f7f7ada113a" }, { - "barId": 274910, + "barId": 275390, "path": "efcore", "remoteUri": "https://github.com/dotnet/efcore", - "commitSha": "fa310bbb8fb21987d6b0916faa01beacc104a3cf" + "commitSha": "74068d2036ea9db2454d62865eef3a1414622538" }, { "barId": 274251, @@ -55,10 +55,10 @@ "commitSha": "4afdc9ffe3543c8fbd2b0b1271624a965bf7f48e" }, { - "barId": 274806, + "barId": 275247, "path": "msbuild", "remoteUri": "https://github.com/dotnet/msbuild", - "commitSha": "f51a5b98ca1efd516dcfdedf23153dfa03a1014d" + "commitSha": "8e5dc5aa32d9dc2e95f52651640dcb28815402ae" }, { "barId": 271771, @@ -67,16 +67,16 @@ "commitSha": "772ee13d2bafaa1414d90dbfbd77e0941115ef19" }, { - "barId": 274804, + "barId": 275165, "path": "razor", "remoteUri": "https://github.com/dotnet/razor", - "commitSha": "ce330d020d1686e80f5ed4aec2e66f12c4f505b5" + "commitSha": "88d10f21004784390b5e220607bd4fa6ebfe1301" }, { - "barId": 274992, + "barId": 275167, "path": "roslyn", "remoteUri": "https://github.com/dotnet/roslyn", - "commitSha": "d3571ef089ef13c74ea786dce8ef615916a097cd" + "commitSha": "c1794aa58db74f27cd6bc7afc1b1d72b713f393b" }, { "barId": 273951, @@ -85,10 +85,10 @@ "commitSha": "714a51c57430dab50b67e5b468016288f5f7b0bd" }, { - "barId": 275054, + "barId": 275332, "path": "runtime", "remoteUri": "https://github.com/dotnet/runtime", - "commitSha": "9c0555e89d91149226cfade4a395e02b8d9401ff" + "commitSha": "6d7ba3656fa078fb17e68b4e31dc9fd9ff9868da" }, { "barId": 273704, @@ -103,10 +103,10 @@ "commitSha": "1414d33c86e760d6635a838a45da0308ff3d53c9" }, { - "barId": 275117, + "barId": 275454, "path": "source-build-reference-packages", "remoteUri": "https://github.com/dotnet/source-build-reference-packages", - "commitSha": "9f13ec67aeca9a84458549b440c1b0e33df71b44" + "commitSha": "e66cb9556591cff76527ba15386357bc2f463c7c" }, { "barId": 274866, @@ -121,16 +121,16 @@ "commitSha": "66c6797a1e9a9346d5f6de3f9edaecad6e4350d0" }, { - "barId": 274065, + "barId": 275177, "path": "templating", "remoteUri": "https://github.com/dotnet/templating", - "commitSha": "57150459685ad38272234d96c17036c2cd2f3b96" + "commitSha": "43d52c423e7ed37cf59a01629f9bace5c6bf6f96" }, { - "barId": 272429, + "barId": 275283, "path": "vstest", "remoteUri": "https://github.com/microsoft/vstest", - "commitSha": "cdcfb7f5c163d7b7a555b129522c3b868f73e92c" + "commitSha": "8b102963a8cce7809a6956fe0a6df5cb93b2447a" }, { "barId": 273947, @@ -139,16 +139,16 @@ "commitSha": "7890fbb7f086ae12b11ca85499b18f460c9090dd" }, { - "barId": 274987, + "barId": 275281, "path": "winforms", "remoteUri": "https://github.com/dotnet/winforms", - "commitSha": "d5b62a713f569a578805301a2bc77e7dfe60b36d" + "commitSha": "6dd129e422c9b14dc835e669688247386bef2f95" }, { - "barId": 274919, + "barId": 275192, "path": "wpf", "remoteUri": "https://github.com/dotnet/wpf", - "commitSha": "46685670c98a9e003ca98df637f68092d3b2cb65" + "commitSha": "900946a8e06533fd7ea8909d91b0c879a7c1f9be" }, { "barId": 274010, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy