Skip to content

Cosmos DB: Find/FindAsync performs SQL API query when entity has embedded entities #24202

Closed

Description

Include your code

When using Cosmos DB + EntityFrameworkCore 5.0.3, the FindAsync and Find methods do not perform the cheaper (w.r.t. RU) document lookup query when there are owned (embedded) entities.

using (var ctx = new TestContext())
{
    await ctx.PackageAggregates.FindAsync(pk, name);
}

...

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var builder = modelBuilder.Entity<PackageAggregate>();
    builder.HasPartitionKey(x => x.PartitionKey);
    builder.Property(x => x.PartitionKey).ToJsonProperty("pk");
    builder.HasKey(x => new { x.PartitionKey, x.LowerName });

    builder.OwnsMany(x => x.Owners); // this line causes the problem
}

When the PackageAggregate entity has no embedded entity, the lookup performs this API call to Cosmos DB. This costs 1 RU.
image

When there is an embedded entity, a SQL API query is made, costing nearly 3 times as much (3.03 RU).
image

Full repro code

using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        public const string Endpoint = "https://<resource name>.documents.azure.com:443/";
        public const string Key = "<key>";
        public const string Database = "PackageManagement";
        public const string Container = "PackagesEF";

        static async Task Main(string[] args)
        {
            var pk = "my-pk";
            var name = Guid.NewGuid().ToString();

            using (var ctx = new TestContext())
            {
                ctx.PackageAggregates.Add(new PackageAggregate
                {
                    PartitionKey = pk,
                    LowerName = name,
                });
                await ctx.SaveChangesAsync();
            }

            using (var ctx = new TestContext())
            {
                var pa = await ctx.PackageAggregates.FindAsync(pk, name);
                Console.WriteLine("Found? " + (pa != null));
            }
        }
    }

    class TestContext : DbContext
    {
        public DbSet<PackageAggregate> PackageAggregates { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseCosmos(Program.Endpoint, Program.Key, Program.Database, o =>
                {
                    // Use this if you want to use a MITM proxy to verify API calls.
                    // o.WebProxy(new WebProxy("localhost", 20000));
                    // o.ConnectionMode(ConnectionMode.Gateway);
                });
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasDefaultContainer(Program.Container);

            var builder = modelBuilder.Entity<PackageAggregate>();
            builder.HasPartitionKey(x => x.PartitionKey);
            builder.Property(x => x.PartitionKey).ToJsonProperty("pk");
            builder.HasKey(x => new { x.PartitionKey, x.LowerName });
            builder.OwnsMany(x => x.Owners);
        }
    }

    public class PackageAggregate
    {
        public string PartitionKey { get; set; }
        public string LowerName { get; set; }
        public List<PackageOwner> Owners { get; set; }
    }

    public class PackageOwner
    {
        public string Username { get; set; }
    }
}

Include provider and version information

EF Core version: 5.0.3
Database provider: Microsoft.EntityFrameworkCore.CosmosDB
Target framework: .NET 5.0
Operating system: Windows 10
IDE: Visual Studio 2019 16.10 Preview 1 internal

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

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions