efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Cosmos: Projecting out owned entities which own additional entity types retrieves the entire document

Open roji opened this issue 1 year ago • 2 comments

public virtual Task Project_owned_reference_navigation_which_owns_additional(bool async)
    => AssertQuery(
        async,
        ss => ss.Set<OwnedPerson>().OrderBy(o => o.Id).Select(p => p.PersonAddress));

Since PersonAddress owns further entity types (OwnedCountry), the expression inside the Select() contains IncludeExpression; this leads CosmosProjectionBindingExpressionVisitor into a specific path where we end up projecting the entire document. Compare how this works in relational - CosmosProjectionBindingExpressionVisitor is one place where Cosmos is still quite behind.

roji avatar Jun 22 '24 19:06 roji

Not actually closed by #34355

roji avatar Aug 04 '24 12:08 roji

@roji This is not limited to owned entities that have additional types. Projecting either an owned reference or collection results in bringing back the entire document. For example:

    await context
        .Set<Customer>()
        .AsNoTracking()
        .Select(e => e.Addresses)
        .ToListAsync();

    await context
        .Set<Customer>()
        .AsNoTracking()
        .Select(e => e.PhoneNumber)
        .ToListAsync();
info: 8/28/2024 12:33:56.712 CosmosEventId.ExecutingSqlQuery[30100] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executing SQL query for container 'SomeDbContext' in partition 'None' [Parameters=[]]
      SELECT VALUE c
      FROM root c
info: 8/28/2024 12:33:56.719 CosmosEventId.ExecutedReadNext[30102] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executed ReadNext (6.7595 ms, 2.25 RU) ActivityId='9616a1c2-9e2a-4858-a426-68e6001d2ac1', Container='SomeDbContext', Partition='None', Parameters=[]
      SELECT VALUE c
      FROM root c
public class Customer
{
    public Guid Id { get; set; }
    public required string Name { get; set; }
    public required string PrimaryCountry { get; set; }
    public required string Region { get; set; }
    public List<string>? Notes { get; set; }
    public Phone PhoneNumber { get; set; }
    public List<Address> Addresses { get; } = new();
}

public class Address
{
    public required string Street { get; set; }
    public required string City { get; set; }
    public required string Postcode { get; set; }
    public required string Country { get; set; }
}

public class Phone
{
    public required int CountryCode { get; set; }
    public required string Number { get; set; }
}

ajcvickers avatar Aug 28 '24 11:08 ajcvickers

@ajcvickers understood and thanks for checking... I think this makes this even more important to fix (in 10).

roji avatar Aug 29 '24 09:08 roji