diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs index 39ae61bda32..2d953aa6d13 100644 --- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs +++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs @@ -598,21 +598,30 @@ protected override Expression VisitSqlParameter(SqlParameterExpression sqlParame var invariantName = sqlParameterExpression.Name; var parameterName = sqlParameterExpression.Name; - if (_relationalCommandBuilder.Parameters - .All( - p => p.InvariantName != parameterName - || (p is TypeMappedRelationalParameter typeMappedRelationalParameter - && (typeMappedRelationalParameter.RelationalTypeMapping.StoreType != sqlParameterExpression.TypeMapping!.StoreType - || typeMappedRelationalParameter.RelationalTypeMapping.Converter - != sqlParameterExpression.TypeMapping!.Converter)))) + // Try to see if a parameter already exists - if so, just integrate the same placeholder into the SQL instead of sending the same + // data twice. + // Note that if the type mapping differs, we do send the same data twice (e.g. the same string may be sent once as Unicode, once as + // non-Unicode). + var parameter = _relationalCommandBuilder.Parameters.FirstOrDefault(p => + p.InvariantName == parameterName + && p is TypeMappedRelationalParameter typeMappedRelationalParameter + && typeMappedRelationalParameter.RelationalTypeMapping.StoreType == sqlParameterExpression.TypeMapping!.StoreType + && typeMappedRelationalParameter.RelationalTypeMapping.Converter == sqlParameterExpression.TypeMapping!.Converter); + + if (parameter is null) { parameterName = GetUniqueParameterName(parameterName); + _relationalCommandBuilder.AddParameter( invariantName, _sqlGenerationHelper.GenerateParameterName(parameterName), sqlParameterExpression.TypeMapping!, sqlParameterExpression.IsNullable); } + else + { + parameterName = ((TypeMappedRelationalParameter)parameter).Name; + } _relationalCommandBuilder .Append(_sqlGenerationHelper.GenerateParameterNamePlaceholder(parameterName)); diff --git a/test/EFCore.Relational.Specification.Tests/Query/GearsOfWarQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/GearsOfWarQueryRelationalTestBase.cs index 360f58481fb..8879e2cdd96 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/GearsOfWarQueryRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/GearsOfWarQueryRelationalTestBase.cs @@ -21,7 +21,7 @@ public virtual Task Parameter_used_multiple_times_take_appropriate_inferred_type var place = "Seattle"; return AssertQuery( async, - ss => ss.Set().Where(e => e.Nation == place || e.Location == place)); + ss => ss.Set().Where(e => e.Nation == place || e.Location == place || e.Location == place)); } public override async Task Correlated_collection_with_distinct_not_projecting_identifier_column_also_projecting_complex_expressions( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index 025617d3a8a..b5d77fa52af 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -9413,7 +9413,7 @@ public override async Task Parameter_used_multiple_times_take_appropriate_inferr SELECT [c].[Name], [c].[Location], [c].[Nation] FROM [Cities] AS [c] -WHERE [c].[Nation] = @__place_0 OR [c].[Location] = @__place_0_1 +WHERE [c].[Nation] = @__place_0 OR [c].[Location] = @__place_0_1 OR [c].[Location] = @__place_0_1 """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs index 48ffd729bb1..648539cedde 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs @@ -11939,7 +11939,7 @@ public override async Task Parameter_used_multiple_times_take_appropriate_inferr SELECT [c].[Name], [c].[Location], [c].[Nation] FROM [Cities] AS [c] -WHERE [c].[Nation] = @__place_0 OR [c].[Location] = @__place_0_1 +WHERE [c].[Nation] = @__place_0 OR [c].[Location] = @__place_0_1 OR [c].[Location] = @__place_0_1 """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index 46434593a10..94fc8da2bf6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -10225,7 +10225,7 @@ public override async Task Parameter_used_multiple_times_take_appropriate_inferr SELECT [c].[Name], [c].[Location], [c].[Nation] FROM [Cities] AS [c] -WHERE [c].[Nation] = @__place_0 OR [c].[Location] = @__place_0_1 +WHERE [c].[Nation] = @__place_0 OR [c].[Location] = @__place_0_1 OR [c].[Location] = @__place_0_1 """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs index 9d6acb19d37..e26fc12d9d5 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs @@ -3252,7 +3252,7 @@ public override async Task Parameter_used_multiple_times_take_appropriate_inferr SELECT [c].[Name], [c].[Location], [c].[Nation], [c].[PeriodEnd], [c].[PeriodStart] FROM [Cities] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [c] -WHERE [c].[Nation] = @__place_0 OR [c].[Location] = @__place_0_1 +WHERE [c].[Nation] = @__place_0 OR [c].[Location] = @__place_0_1 OR [c].[Location] = @__place_0_1 """); } @@ -9794,7 +9794,7 @@ public override async Task EF_Property_based_Include_navigation_on_derived_type( """); } - // Sequence contains no elements due to temporal filter + // Sequence contains no elements due to temporal filter public override Task ElementAt_basic_with_OrderBy(bool async) => Task.CompletedTask; diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs index 48091ecfc76..4dbe4b83639 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs @@ -2903,7 +2903,7 @@ public override async Task Parameter_used_multiple_times_take_appropriate_inferr SELECT "c"."Name", "c"."Location", "c"."Nation" FROM "Cities" AS "c" -WHERE "c"."Nation" = @__place_0 OR "c"."Location" = @__place_0 +WHERE "c"."Nation" = @__place_0 OR "c"."Location" = @__place_0 OR "c"."Location" = @__place_0 """); }