Skip to content

Cosmos: Projection binding initialize empty collections#37971

Merged
roji merged 4 commits intodotnet:mainfrom
JoasE:fix/#36577
Mar 23, 2026
Merged

Cosmos: Projection binding initialize empty collections#37971
roji merged 4 commits intodotnet:mainfrom
JoasE:fix/#36577

Conversation

@JoasE
Copy link
Copy Markdown
Contributor

@JoasE JoasE commented Mar 23, 2026

During projection binding, IncludeCollection is invoked which will enumerate a select over a projection binding lambda for the collection type. If tracking is enabled, change tracking will initialize the collection if not set during the enumeration. If tracking is not enabled, a fixup is invoked which will initialize the collection if not set. If no items are in the list, no enumeration is done and no fixup or change tracking code is ran, thus the collection will never be initialized.

Simple fix is always initialize collection in IncludeCollection if value is not null

Closes: #36577

  • I've read the guidelines for contributing and seen the walkthrough
  • I've posted a comment on an issue with a detailed description of how I am planning to contribute and got approval from a member of the team
  • The code builds and tests pass locally (also verified by our automated build checks)
  • Commit messages follow this format:
        Summary of the changes
        - Detail 1
        - Detail 2

        Fixes #bugnumber
  • Tests for the changes have been added (for bug fixes / features)
  • Code follows the same patterns and style as existing code in this repo

@JoasE JoasE changed the title Cosmos: Initialize empty collections Cosmos: Projection binding initialize empty collections Mar 23, 2026
@JoasE JoasE marked this pull request as ready for review March 23, 2026 13:21
@JoasE JoasE requested a review from a team as a code owner March 23, 2026 13:21
Copilot AI review requested due to automatic review settings March 23, 2026 13:21
@JoasE JoasE marked this pull request as draft March 23, 2026 13:21
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Cosmos projection materialization so that included/projection-bound empty collection navigations are initialized as empty collections (instead of remaining null), allowing the owned-navigation association tests to use the shared base asserters again.

Changes:

  • Initialize collection navigations when relatedEntities is non-null (including the empty-enumerable case) during include materialization.
  • Remove the Cosmos-specific test asserter overrides that were compensating for #36577.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
test/EFCore.Cosmos.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsCosmosFixture.cs Removes workaround asserters for #36577 now that Cosmos behavior is corrected.
src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs Ensures collection navigations are created for materialization when the related-entities enumerable exists (even if empty), and removes the prior initialize delegate plumbing.

@JoasE JoasE marked this pull request as ready for review March 23, 2026 18:30
Copilot AI review requested due to automatic review settings March 23, 2026 18:30
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Copy link
Copy Markdown
Member

@roji roji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @JoasE, looks great.

I'll add a breaking change note for this.

/cc @AndriySvyryd let me know if you have any memory around this, or any objection to this fix (which is a breaking change, though a bug fix).

@roji roji enabled auto-merge (squash) March 23, 2026 19:58
@JoasE
Copy link
Copy Markdown
Contributor Author

JoasE commented Mar 23, 2026

@AndriySvyryd @roji Good to note that both before and after this change a null value in the json will not be materialized to null but to an empty collection. Not sure if that is a bug or not. Should I create an issue?
On main:

[ConditionalFact]
public virtual async Task Can_set_embedded_collection_null()
{
    var options = await Fixture.CreateOptions(seed: false);
    var person = new Person
    {
        Id = 1,
        Addresses = null,
    };
    using var context = new EmbeddedTransportationContext(options);
    await context.AddAsync(person);
    await context.SaveChangesAsync();
    context.ChangeTracker.Clear();
    var entity = await context.Set<Person>().FirstAsync();
    Assert.Null(entity.Addresses); // Fails: Assert.Null() Failure: Value is not null, Actual: []
}

@roji
Copy link
Copy Markdown
Member

roji commented Mar 23, 2026

Good to note that both before and after this change a null value in the json will not be materialized to null but to an empty collection. Not sure if that is a bug or not. Should I create an issue?

Hmm, I don't think we have clear behavior in this case - since EF shouldn't ever produce a null JSON property for a collection (but only an empty JSON array), this is "undefined". It's good to have an issue for thinking what we want to do here: I'd check (a) what our relational JSON behavior does, and (b) what System.Text.Json does.

@roji roji merged commit 9817145 into dotnet:main Mar 23, 2026
14 of 15 checks passed
@AndriySvyryd
Copy link
Copy Markdown
Member

/cc @AndriySvyryd let me know if you have any memory around this, or any objection to this fix (which is a breaking change, though a bug fix).

No objections.

Good to note that both before and after this change a null value in the json will not be materialized to null but to an empty collection. Not sure if that is a bug or not. Should I create an issue?

Hmm, I don't think we have clear behavior in this case - since EF shouldn't ever produce a null JSON property for a collection (but only an empty JSON array), this is "undefined". It's good to have an issue for thinking what we want to do here: I'd check (a) what our relational JSON behavior does, and (b) what System.Text.Json does.

We don't support it yet. #35916

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cosmos: projection of empty owned entity lists returns null instead of empty

4 participants