dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.65k stars 3.15k forks source link

Cosmos: Selecting complex properties (object or collections mapped as "OwnsOne" and "OwnsMany") makes the query return the entire document #27440

Open alexandrevribeiro opened 2 years ago

alexandrevribeiro commented 2 years ago

I'm using Microsoft.EntityFrameworkCore.Cosmos 6.0.1 to perform queries against a Cosmos DB, but the problem I'm facing is that whenever I include in the Select any property that is either an object or a collection/array (mapped as "OwnsOne" and "OwnsMany"), instead of querying only the selected properties, it actually selects the entire document for EF to be able to select the complex/collection property.

Code sample

Entities

public record Litigation
{
    public string Id { get; set; }
    public string IdentifierNumber { get; set; }
    public GenericEntity City { get; set; }
    public List<GenericEntity> Subjects { get; set; }
}

public record GenericEntity
{  
    public int Id { get; set; } 
    public string Name { get; set; }
}

Entity type configuration

Note: There might be probably a way to avoid using ToJsonProperty for each Cosmos camelCase property (maybe using serialized options), but I'm not worried about that for now.

public class LitigationTypeConfiguration : IEntityTypeConfiguration<Litigation>
{
    public void Configure(EntityTypeBuilder<Litigation> builder)
    {
        builder.ToContainer(CosmosConstants.LitigationContainerName)
           .HasPartitionKey(l => l.TenantId)
           .HasNoDiscriminator();

        builder.Property(t => t.Id).ToJsonProperty("id").IsRequired();
        builder.Property(t => t.IdentifierNumber).ToJsonProperty("identifierNumber");
        builder.OwnsOne(t => t.City,
            o =>
            {
                o.ToJsonProperty("city");
                o.Property(p => p.Id).ToJsonProperty("id");
                o.Property(p => p.Name).ToJsonProperty("name");
            });
        builder.OwnsMany(t => t.Subjects,
            o =>
            {
                o.ToJsonProperty("subjects");
                o.Property(p => p.Id).ToJsonProperty("id");
                o.Property(p => p.Name).ToJsonProperty("name");
            });
    }
}

Queries sample and Results

Query sample 1) Selecting only simple properties

Query sample:

var test = _context.Litigations
    .Where(l => l.Id == id)
    .WithPartitionKey("MyPartitionKey")
    .AsNoTracking()
    .Select(l => new
    {
        l.Id,
        l.IdentifierNumber,
    }).ToList();

Result:

Note that only the c["id"] and c["identifierNumber"] were selected, as expected. image

Query sample 2) Selecting a complex object property (city)

Query sample:

var test = _context.Litigations
    .Where(l => l.Id == id)
    .WithPartitionKey("MyPartitionKey")
    .AsNoTracking()
    .Select(l => new
    {
        l.Id,
        l.IdentifierNumber,
        l.City // <-------------
    }).ToList();

Result:

In this case, the entire document is being selected (represented in the result as c), which shouldn't happen. If I copy and past this query on Azure Portal Cosmos Data Explorer, I can see it is indeed returning the entire document: image

Query sample 3) Selecting a collection/array property (subjects)

Query sample:

var test = _context.Litigations
    .Where(l => l.Id == id)
    .WithPartitionKey("MyPartitionKey")
    .AsNoTracking()
    .Select(l => new
    {
        l.Id,
        l.IdentifierNumber,
        l.Subjects // <-------------
    }).ToList();

Result:

The same behavior happening when selecting a complex object is also happening with a collection property: image

Provider and version information

EF Core version: 6.0.1 Database provider: Microsoft.EntityFrameworkCore.Cosmos 6.0.1 Target framework: .NET 6.0 Operating system: Windows IDE: Visual Studio Professional 2022

AndriySvyryd commented 2 years ago

Related to https://github.com/dotnet/efcore/issues/16926

For tracking queries we need to select the owner (the document in this case), however this shouldn't be necessary for non-tracking queries

samisq commented 1 year ago

+1 for this. We're observing the same issue with a rational provider (we're using Npgsql.EntityFrameworkCore.PostgreSQL v7.0).

relfman-cmg commented 1 year ago

I would also +1 this as well