Repository navigation
Azure Cosmos DB provider should default to implicit ownership #24803
Description
The typical case for document databases is to store a root entity and serialize its children, including single and collection navigations, as sub-properties rather than separately tracked entities. The EF Core provider for Azure Cosmos DB currently has two issues with this convention. First, complex entities in the graph are assumed to be independently tracked and therefore modeling the default behavior requires explicitly configuring ownership. For larger graphs this has a negative impact on maintainability. Second, complex entities shouldn't have to be tracked independently. They should be considered serializable types owned by the root entity that are serialized directly.
As a solution, I'd like to see:
- When I configure an entity for Cosmos DB as DbSet any complex types or collections in the graph are assumed to be owned by the parent entity. Let me opt out by configuration but don't force me to opt in.
- Instead of forcing complex types to be tracked as separate entities, treat them the same way as primitives, i.e. serialize and deserialize directly instead of tracking separately. This way their properties are still indexed and queryable.
Example from Cosmos DB getting started app:
namespace todo
{
public class Family
{
[JsonPropertyName("id")]
public string Id { get; set; }
public string LastName { get; set; }
public IList<Parent> Parents { get; set; }
public IList<Child> Children { get; set; }
public Address Address { get; set; }
public bool IsRegistered { get; set; }
public override string ToString()
{
return JsonSerializer.Serialize(this);
}
}
public class Parent
{
public string FamilyName { get; set; }
public string FirstName { get; set; }
}
public class Child
{
public string FamilyName { get; set; }
public string FirstName { get; set; }
public string Gender { get; set; }
public int Grade { get; set; }
public IList<Pet> Pets { get; set; }
}
public class Pet
{
public string GivenName { get; set; }
}
public class Address
{
public string State { get; set; }
public string County { get; set; }
public string City { get; set; }
}
}Currently I have to do this configuration, whereas with the SDK v4 it "just works":
builder.Entity<Family>()
.HasPartitionKey(nameof(Family.LastName))
.OwnsMany(f => f.Parents);
builder.Entity<Family>()
.OwnsMany(f => f.Children)
.OwnsMany(c => c.Pets);
builder.Entity<Family>()
.OwnsOne(f => f.Address);OwnsMany is fine at one level but gets complicated when there are multiple nested complex types. Using a value converter makes me lose the ability to query on the summary. It would be great if we could just configure the Family as a DbSet and only have to configure the partition key.
Activity
AndriySvyryd commentedon Apr 29, 2021
Related - #6787 to allow configuring owned types without using deeply nested lambdas.
Default to ownership for Cosmos
Default relationships to ownership for Cosmos
Default relationships to ownership for Cosmos
Default relationships to ownership for Cosmos
3 remaining items
Default relationships to ownership for Cosmos
AndriySvyryd commentedon Jul 21, 2021
@ACROV You are reading correctly. #17306 will allow value converters that output raw JSON, though it means that all complex types will become a black box for query.
You shouldn't need to create any keys for owned entities, EF Core creates shadow keys by convention that are not persisted, if there's an issue with them please file it separately.
John-HLC commentedon Nov 19, 2021
@AndriySvyryd, @ajcvickers, @JeremyLikness - Apologies in advance for my limited EF Core for Cosmos understanding.. if you can reach me half way...
The problem i think we ran across is that we do not keep a persistent DB context when using Blazor WASM (Client only) app.
Using the DBContext - we get all the data needed.. and use it disconnected from the DBContext.. then if we need to update data, we new up a new DBContext() and use the Update() command to update the entity. This I believe is recommended way to do things in Blazor WASM client only (not keeping the DbContext alive).
So in this case.. should we be adding an "Id" to every owned class going forward?
Or do you think we should be doing below as the recommended way to update Entities with owned content nested?
John-HLC commentedon Nov 19, 2021
@ajcvickers - in this post you mentioned one could add a private CLR property to the owned entity. #19856
can you expand on this - what does it look like? We tried but did not work for us.
AndriySvyryd commentedon Nov 19, 2021
If that works, then yes. In most cases EF will generate the values for the shadow keys on owned entity types. So, unless something is failing you don't need to preserve them.
John-HLC commentedon Nov 19, 2021
I know it's a small thing but my brain kind of hurts... when needing to update an entity
to code "Add"
and then on another line mark it as "Modified".
Makes me feel like I'm going off course on a work round, and from readability standpoint reading Add feels wrong when really updating (even if that's what its doing under the covers).
Would it be possible to introduce a new method on Microsoft.EntityFrameworkCore.Cosmos SDK DB Context... and name that method UpdateDisconnectedEntity() or something like that?
Or add the "Add" and "Mark as Modified" somewhere in documentation for Microsoft.EntityFrameworkCore.Cosmos for BlazorWASM.
@JeremyLikness - thoughts? since you work with Blazor WASM.
AndriySvyryd commentedon Nov 19, 2021
@John-HLC The workaround is needed because EF currently doesn't generate values when an entity is marked as Modified, #6999 tracks this