From 6bde890b6807ed30db4d25e04aa1a46ab1472c6e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Jul 2026 00:25:04 +0000 Subject: [PATCH 1/3] Document Cosmos undefined projections breaking change Document https://github.com/dotnet/EntityFramework.Docs/issues/5418 --- .../core/providers/cosmos/querying.md | 23 +++++++ .../ef-core-11.0/breaking-changes.md | 60 +++++++++++++++++++ .../core/what-is-new/ef-core-11.0/whatsnew.md | 19 ++++++ 3 files changed, 102 insertions(+) diff --git a/entity-framework/core/providers/cosmos/querying.md b/entity-framework/core/providers/cosmos/querying.md index ccf7b92eee..ecc20f7b3a 100644 --- a/entity-framework/core/providers/cosmos/querying.md +++ b/entity-framework/core/providers/cosmos/querying.md @@ -213,6 +213,29 @@ Note that to filter out documents where a value is missing: + +```csharp +var results = await context.Entities + .Where(x => EF.Functions.IsDefined(x.Associate!.NestedAssociate!.Id)) + .Select(x => new { x.Associate!.NestedAssociate!.Id }) + .ToListAsync(); +``` + +Alternatively, use to substitute a default value for any property that could be `undefined`: + +```csharp +var results = await context.Entities + .Select(x => new { Id = EF.Functions.CoalesceUndefined(x.Associate!.NestedAssociate!.Id, Guid.Empty) }) + .ToListAsync(); +``` + ## Function mappings This section shows which .NET methods and members are translated into which SQL functions when querying with the Azure Cosmos DB provider. diff --git a/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md index f4e3c56230..53dd7b3d6a 100644 --- a/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md +++ b/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md @@ -23,6 +23,7 @@ This page documents API and behavior changes that have the potential to break ex |:--------------------------------------------------------------------------------------------------------------- | -----------| | [Sync I/O via the Azure Cosmos DB provider has been fully removed](#cosmos-nosync) | Medium | | [Microsoft.Data.SqlClient has been updated to 7.0](#sqlclient-7) | Medium | +| [Cosmos: exception thrown when a projection evaluates to undefined](#cosmos-undefined-projection) | Medium | | [Cosmos: illegal `id` characters are no longer escaped](#cosmos-no-id-escape) | Medium | | [SQL Server compatibility level now defaults to 160](#sqlserver-compatibility-level-160) | Low | | [EF Core now throws by default when no migrations are found](#migrations-not-found) | Low | @@ -126,6 +127,65 @@ If your application uses composite keys whose values can contain the characters - **Existing data**: Documents previously stored in Cosmos DB have `id` values using the old escape sequences (e.g. `Post|1|^2F`). After upgrading to EF Core 11, EF will generate unescaped `id` values (e.g. `Post|1|/`) and will no longer find those existing documents. To continue accessing existing data without migration, opt back into the old behavior using the `AppContext` switch described above—however, be aware that the id-collision bug will still be present. - **New data**: If you are creating a new application or database, avoid using these illegal characters in key values, as they are not valid in Cosmos DB resource `id` values. See the [Azure documentation](xref:Microsoft.Azure.Documents.Resource.Id) for details. + + +### Cosmos: exception thrown when a projection evaluates to undefined + +[Tracking Issue #38550](https://github.com/dotnet/efcore/pull/38550) + +#### Old behavior + +Previously, when projecting properties in anonymous type or DTO projections via navigation over optional relationships where a segment of the path was absent in the Cosmos DB document (causing the projected value to be `undefined`), the behavior was inconsistent: + +- With **single-property** anonymous type or DTO projections, EF translated the query using `SELECT VALUE`, which silently filtered out any documents where the projected value was `undefined`. This meant fewer results were returned than expected, with no indication of the missing data. +- With **multi-property** anonymous type or DTO projections, an `InvalidOperationException` with the message "Nullable object must have a value" was thrown. + +For example, given an entity `Entity` with an optional owned `Associate` which in turn has an optional owned `NestedAssociate`: + +```csharp +// Previously silently returned fewer results (undefined results were filtered out) +var singlePropResults = await context.Entities + .Select(x => new { x.Associate!.NestedAssociate!.Id }) + .ToListAsync(); + +// Previously threw InvalidOperationException: Nullable object must have a value +var multiPropResults = await context.Entities + .Select(x => new { x.Associate!.NestedAssociate!.Id, x.Associate.NestedAssociate.String }) + .ToListAsync(); +``` + +#### New behavior + +Starting with EF Core 11.0, an `InvalidOperationException` is thrown in both cases when any part of the projection evaluates to `undefined` in Azure Cosmos DB. The exception message is: + +> A part of the projection was undefined, use the coalesce operator to handle possible undefined values. + +#### Why + +The previous behavior was inconsistent. Single-property projections could silently discard results, making it easy to miss data without any indication of the problem. The new behavior ensures consistent, predictable error reporting whenever a projection encounters an undefined value. + +#### Mitigations + +Use to filter out documents where the projected value is missing: + +```csharp +var results = await context.Entities + .Where(x => EF.Functions.IsDefined(x.Associate!.NestedAssociate!.Id)) + .Select(x => new { x.Associate!.NestedAssociate!.Id }) + .ToListAsync(); +``` + +Alternatively, use to provide a default value for properties that could be `undefined`: + +```csharp +var results = await context.Entities + .Select(x => new + { + Id = EF.Functions.CoalesceUndefined(x.Associate!.NestedAssociate!.Id, Guid.Empty) + }) + .ToListAsync(); +``` + ## Low-impact changes diff --git a/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md b/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md index 8aef322ac0..210e20c29d 100644 --- a/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md +++ b/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md @@ -791,6 +791,25 @@ For more information, [see the documentation](xref:core/providers/cosmos/saving# This feature was contributed by [@JoasE](https://github.com/JoasE) - many thanks! + + +### Consistent behavior for undefined values in projections + +Previously, when projecting scalar properties via optional navigations where a document path was absent (resulting in an `undefined` value in Cosmos DB), behavior was inconsistent: single-property anonymous type projections silently dropped those results, while multi-property projections threw a cryptic exception. + +EF Core 11 now consistently throws an `InvalidOperationException` when any part of a projection evaluates to `undefined`. Use to filter or to provide fallbacks: + +```csharp +var results = await context.Entities + .Where(x => EF.Functions.IsDefined(x.Associate!.NestedAssociate!.Id)) + .Select(x => new { x.Associate!.NestedAssociate!.Id }) + .ToListAsync(); +``` + +For more information, see the [breaking changes documentation](xref:core/what-is-new/ef-core-11.0/breaking-changes#cosmos-undefined-projection). + +This feature was contributed by [@JoasE](https://github.com/JoasE) - many thanks! + ## Migrations From 49c1974569d140f80182fd7bb9ac559d21211f76 Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Tue, 28 Jul 2026 16:17:54 -0700 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../core/what-is-new/ef-core-11.0/breaking-changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md index 53dd7b3d6a..296a549c84 100644 --- a/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md +++ b/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md @@ -150,7 +150,7 @@ var singlePropResults = await context.Entities // Previously threw InvalidOperationException: Nullable object must have a value var multiPropResults = await context.Entities - .Select(x => new { x.Associate!.NestedAssociate!.Id, x.Associate.NestedAssociate.String }) + .Select(x => new { x.Associate!.NestedAssociate!.Id, x.Associate!.NestedAssociate!.String }) .ToListAsync(); ``` From 35ff36a42d877b5387a813732cfe80e1d9b5bb28 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Jul 2026 23:20:19 +0000 Subject: [PATCH 3/3] Address review feedback: fix tracking issue link and add naked projection docs --- .../core/providers/cosmos/querying.md | 22 +++++++++++++++++++ .../ef-core-11.0/breaking-changes.md | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/entity-framework/core/providers/cosmos/querying.md b/entity-framework/core/providers/cosmos/querying.md index ecc20f7b3a..3ca86a582a 100644 --- a/entity-framework/core/providers/cosmos/querying.md +++ b/entity-framework/core/providers/cosmos/querying.md @@ -236,6 +236,28 @@ var results = await context.Entities .ToListAsync(); ``` +### Naked projections and SELECT VALUE + +A _naked projection_ — where a single value is projected directly without wrapping it in a DTO or anonymous type — is translated using `SELECT VALUE` in Cosmos DB SQL. As a result, any documents where the projected value is `undefined` are **silently skipped** and not included in the results: + +```csharp +// Naked projection - translated as SELECT VALUE, undefined results are silently omitted +var ids = await context.Entities + .Select(x => x.Associate!.NestedAssociate!.Id) + .ToListAsync(); +``` + +In contrast, any top-level instantiation in the projection (anonymous type, DTO, entity, or complex type) does **not** use `SELECT VALUE`. When a part of such a projection is `undefined`, an `InvalidOperationException` is thrown as described above. + +If silently skipping undefined results is not the desired behavior, wrap the projected value in an anonymous type or DTO to get a consistent error instead: + +```csharp +// Wrapped in an anonymous type - does not use SELECT VALUE, throws if undefined +var results = await context.Entities + .Select(x => new { x.Associate!.NestedAssociate!.Id }) + .ToListAsync(); +``` + ## Function mappings This section shows which .NET methods and members are translated into which SQL functions when querying with the Azure Cosmos DB provider. diff --git a/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md index 296a549c84..ab8de1318f 100644 --- a/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md +++ b/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md @@ -131,7 +131,7 @@ If your application uses composite keys whose values can contain the characters ### Cosmos: exception thrown when a projection evaluates to undefined -[Tracking Issue #38550](https://github.com/dotnet/efcore/pull/38550) +[Tracking Issue #34067](https://github.com/dotnet/efcore/issues/34067) #### Old behavior