From 0bf0e8edd215b134ccc773b86f3604eff2d337cf Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 17 Nov 2025 21:14:49 +0000
Subject: [PATCH 1/5] Initial plan
From e31cf879fbe4e069ae5f3914bc207e40f22b2a96 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 17 Nov 2025 21:23:01 +0000
Subject: [PATCH 2/5] Add breaking change note for parameterized collections in
EF Core 10
Co-authored-by: roji <1862641+roji@users.noreply.github.com>
---
.../ef-core-10.0/breaking-changes.md | 74 +++++++++++++++++++
1 file changed, 74 insertions(+)
diff --git a/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
index d4b2d07d17..f6d3a23f7f 100644
--- a/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
+++ b/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
@@ -24,6 +24,7 @@ This page documents API and behavior changes that have the potential to break ex
|:--------------------------------------------------------------------------------------------------------------- | -----------|
| [Application Name is now injected into the connection string](#sqlserver-application-name) | Low |
| [SQL Server json data type used by default on Azure SQL and compatibility level 170](#sqlserver-json-data-type) | Low |
+| [Parameterized collections now use multiple parameters by default](#parameterized-collections) | Low |
| [ExecuteUpdateAsync now accepts a regular, non-expression lambda](#ExecuteUpdateAsync-lambda) | Low |
| [Complex type column names are now uniquified](#complex-type-column-uniquification) | Low |
| [Nested complex type properties use full path in column names](#nested-complex-type-column-names) | Low |
@@ -132,6 +133,79 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
}
```
+
+
+### Parameterized collections now use multiple parameters by default
+
+[Tracking Issue #36368](https://github.com/dotnet/efcore/issues/36368)
+
+#### Old behavior
+
+In EF Core 9 and earlier, parameterized collections in LINQ queries (such as those used with `.Contains()`) were translated to SQL using a JSON array parameter with `OPENJSON` on SQL Server:
+
+```c#
+int[] ids = [1, 2, 3];
+var blogs = await context.Blogs.Where(b => ids.Contains(b.Id)).ToListAsync();
+```
+
+This generated SQL like:
+
+```sql
+@__ids_0='[1,2,3]'
+
+SELECT [b].[Id], [b].[Name]
+FROM [Blogs] AS [b]
+WHERE [b].[Id] IN (
+ SELECT [i].[value]
+ FROM OPENJSON(@__ids_0) WITH ([value] int '$') AS [i]
+)
+```
+
+#### New behavior
+
+Starting with EF Core 10.0, parameterized collections are now translated using multiple scalar parameters by default:
+
+```sql
+SELECT [b].[Id], [b].[Name]
+FROM [Blogs] AS [b]
+WHERE [b].[Id] IN (@ids1, @ids2, @ids3)
+```
+
+When the number of values in the collection approaches limits (e.g., SQL Server's 2,100 parameter limit), this can cause runtime errors such as `System.DivideByZeroException` or exceed database parameter limits.
+
+EF also "pads" the parameter list to reduce the number of different SQL statements generated. For example, a collection with 8 values generates SQL with 10 parameters, with the extra parameters containing duplicate values.
+
+#### Why
+
+The new default translation provides the query planner with cardinality information about the collection, which can lead to better query plans in many scenarios. The multiple parameter approach balances between plan cache efficiency (by parameterizing) and query optimization (by providing cardinality).
+
+However, different workloads may benefit from different translation strategies depending on collection sizes, query patterns, and database characteristics.
+
+#### Mitigations
+
+If you encounter issues with the new default behavior (such as parameter limit errors or performance regressions), you can configure the translation mode globally:
+
+```c#
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => optionsBuilder
+ .UseSqlServer("",
+ o => o.UseParameterizedCollectionMode(ParameterTranslationMode.Constant));
+```
+
+Available modes are:
+- `ParameterTranslationMode.MultipleParameters` - The new default (multiple scalar parameters)
+- `ParameterTranslationMode.Constant` - Inlines values as constants (pre-EF8 behavior)
+- `ParameterTranslationMode.SingleJsonParameter` - Uses JSON array parameter (EF8-9 default)
+
+You can also control the translation on a per-query basis:
+
+```c#
+// Use constants instead of parameters for this specific query
+var blogs = await context.Blogs.Where(b => EF.Constant(ids).Contains(b.Id)).ToListAsync();
+```
+
+For more information about parameterized collection translation, [see the documentation](xref:core/what-is-new/ef-core-10.0/whatsnew#improved-translation-for-parameterized-collection).
+
### ExecuteUpdateAsync now accepts a regular, non-expression lambda
From 98814532fdd9c3443a53607c4c89cdc82e948b1e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 17 Nov 2025 21:24:41 +0000
Subject: [PATCH 3/5] Fix xref link to correct anchor in whatsnew documentation
Co-authored-by: roji <1862641+roji@users.noreply.github.com>
---
.../core/what-is-new/ef-core-10.0/breaking-changes.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
index f6d3a23f7f..e791d7b8d3 100644
--- a/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
+++ b/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
@@ -204,7 +204,7 @@ You can also control the translation on a per-query basis:
var blogs = await context.Blogs.Where(b => EF.Constant(ids).Contains(b.Id)).ToListAsync();
```
-For more information about parameterized collection translation, [see the documentation](xref:core/what-is-new/ef-core-10.0/whatsnew#improved-translation-for-parameterized-collection).
+For more information about parameterized collection translation, [see the documentation](xref:core/what-is-new/ef-core-10.0/whatsnew#parameterized-collection-translation).
From cb33ab05c7b5e24ddacc0ca984187482c724f2bf Mon Sep 17 00:00:00 2001
From: Shay Rojansky
Date: Mon, 17 Nov 2025 22:41:35 +0100
Subject: [PATCH 4/5] Fixup
---
.../ef-core-10.0/breaking-changes.md | 29 ++++++++++++-------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
index e791d7b8d3..a4947b496e 100644
--- a/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
+++ b/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
@@ -141,14 +141,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
#### Old behavior
-In EF Core 9 and earlier, parameterized collections in LINQ queries (such as those used with `.Contains()`) were translated to SQL using a JSON array parameter with `OPENJSON` on SQL Server:
+In EF Core 9 and earlier, parameterized collections in LINQ queries (such as those used with `.Contains()`) were translated to SQL using a JSON array parameter by default. Consider the following query:
```c#
int[] ids = [1, 2, 3];
var blogs = await context.Blogs.Where(b => ids.Contains(b.Id)).ToListAsync();
```
-This generated SQL like:
+On SQL Server, this generated the following SQL:
```sql
@__ids_0='[1,2,3]'
@@ -171,10 +171,6 @@ FROM [Blogs] AS [b]
WHERE [b].[Id] IN (@ids1, @ids2, @ids3)
```
-When the number of values in the collection approaches limits (e.g., SQL Server's 2,100 parameter limit), this can cause runtime errors such as `System.DivideByZeroException` or exceed database parameter limits.
-
-EF also "pads" the parameter list to reduce the number of different SQL statements generated. For example, a collection with 8 values generates SQL with 10 parameters, with the extra parameters containing duplicate values.
-
#### Why
The new default translation provides the query planner with cardinality information about the collection, which can lead to better query plans in many scenarios. The multiple parameter approach balances between plan cache efficiency (by parameterizing) and query optimization (by providing cardinality).
@@ -183,7 +179,7 @@ However, different workloads may benefit from different translation strategies d
#### Mitigations
-If you encounter issues with the new default behavior (such as parameter limit errors or performance regressions), you can configure the translation mode globally:
+If you encounter issues with the new default behavior (such as performance regressions), you can configure the translation mode globally:
```c#
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
@@ -193,15 +189,28 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
```
Available modes are:
+
- `ParameterTranslationMode.MultipleParameters` - The new default (multiple scalar parameters)
-- `ParameterTranslationMode.Constant` - Inlines values as constants (pre-EF8 behavior)
-- `ParameterTranslationMode.SingleJsonParameter` - Uses JSON array parameter (EF8-9 default)
+- `ParameterTranslationMode.Constant` - Inlines values as constants (pre-EF8 default behavior)
+- `ParameterTranslationMode.Parameter` - Uses JSON array parameter (EF8-9 default)
You can also control the translation on a per-query basis:
```c#
// Use constants instead of parameters for this specific query
-var blogs = await context.Blogs.Where(b => EF.Constant(ids).Contains(b.Id)).ToListAsync();
+var blogs = await context.Blogs
+ .Where(b => EF.Constant(ids).Contains(b.Id))
+ .ToListAsync();
+
+// Use a single parameter (e.g. JSON parameter with OPENJSON) instead of parameters for this specific query
+var blogs = await context.Blogs
+ .Where(b => EF.Parameter(ids).Contains(b.Id))
+ .ToListAsync();
+
+// Use a multiple scalar parameters for this specific query. This is the default in EF 10, but is useful if the default was changed globally:
+var blogs = await context.Blogs
+ .Where(b => EF.Parameter(ids).Contains(b.Id))
+ .ToListAsync();
```
For more information about parameterized collection translation, [see the documentation](xref:core/what-is-new/ef-core-10.0/whatsnew#parameterized-collection-translation).
From 8d1d0dad3c115751395cbb5f2cf7f1bbf0f8945a Mon Sep 17 00:00:00 2001
From: Shay Rojansky
Date: Fri, 21 Nov 2025 13:29:58 +0100
Subject: [PATCH 5/5] Address review comments
---
.../core/what-is-new/ef-core-10.0/breaking-changes.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
index a4947b496e..8510c58550 100644
--- a/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
+++ b/entity-framework/core/what-is-new/ef-core-10.0/breaking-changes.md
@@ -207,9 +207,9 @@ var blogs = await context.Blogs
.Where(b => EF.Parameter(ids).Contains(b.Id))
.ToListAsync();
-// Use a multiple scalar parameters for this specific query. This is the default in EF 10, but is useful if the default was changed globally:
+// Use multiple scalar parameters for this specific query. This is the default in EF 10, but is useful if the default was changed globally:
var blogs = await context.Blogs
- .Where(b => EF.Parameter(ids).Contains(b.Id))
+ .Where(b => EF.MultipleParameters(ids).Contains(b.Id))
.ToListAsync();
```