Skip to content

Commit b3078cb

Browse files
committed
Document SQL Server JSON_CONTAINS() (#5260)
See dotnet/efcore#36656
1 parent 29f3f69 commit b3078cb

1 file changed

Lines changed: 46 additions & 0 deletions

File tree

  • entity-framework/core/what-is-new/ef-core-11.0

entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,49 @@ var results = await context.Blogs
257257
Both methods return `FullTextSearchResult<TEntity>`, giving you access to both the entity and the ranking value from SQL Server's full-text engine. This allows for more sophisticated result ordering and filtering based on relevance scores.
258258

259259
For more information, see the [full documentation on full-text search](xref:core/providers/sql-server/full-text-search).
260+
261+
<a name="sql-server-json-contains"></a>
262+
263+
### Translate Contains over primitive collections using JSON_CONTAINS
264+
265+
SQL Server 2025 introduced the [`JSON_CONTAINS`](/sql/t-sql/functions/json-contains-transact-sql) function, which checks whether a value exists in a JSON document. Starting with EF Core 11, when targeting SQL Server 2025, LINQ `Contains` queries over primitive (or scalar) collections stored as JSON are translated to use this function, replacing the previous, less efficient `OPENJSON`-based translation.
266+
267+
The following query checks whether a blog's `Tags` collection contains a specific tag:
268+
269+
```csharp
270+
var blogs = await context.Blogs
271+
.Where(b => b.Tags.Contains("ef-core"))
272+
.ToListAsync();
273+
```
274+
275+
Before EF 11 - or when targeting older SQL Server versions - this generates the following SQL:
276+
277+
```sql
278+
SELECT [b].[Id], [b].[Name], [b].[Tags]
279+
FROM [Blogs] AS [b]
280+
WHERE N'ef-core' IN (
281+
SELECT [t].[value]
282+
FROM OPENJSON([b].[Tags]) WITH ([value] nvarchar(max) '$') AS [t]
283+
)
284+
```
285+
286+
With EF 11, configure EF to target SQL Server 2025 by setting the compatibility level as follows:
287+
288+
```csharp
289+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
290+
=> optionsBuilder
291+
.UseSqlServer("<CONNECTION STRING>", o => o.UseCompatibilityLevel(170));
292+
```
293+
294+
At that point, EF will instead generate the following SQL:
295+
296+
```sql
297+
SELECT [b].[Id], [b].[Name], [b].[Tags]
298+
FROM [Blogs] AS [b]
299+
WHERE JSON_CONTAINS([b].[Tags], 'ef-core') = 1
300+
```
301+
302+
`JSON_CONTAINS()` can notably make use of a [JSON index](/sql/t-sql/statements/create-json-index-transact-sql), if one is defined.
303+
304+
> [!NOTE]
305+
> `JSON_CONTAINS` does not support searching for null values. As a result, this translation is only applied when EF can determine that at least one side is non-nullable — either the item being searched for (e.g. a non-null constant or a non-nullable column), or the collection's elements. When this cannot be determined, EF falls back to the previous `OPENJSON`-based translation.

0 commit comments

Comments
 (0)