Cosmos: Add DatabaseFacade extension Deserialize#37543
Cosmos: Add DatabaseFacade extension Deserialize#37543JoasE wants to merge 11 commits intodotnet:mainfrom
Conversation
|
This is a bit "hacky", but it's the simplest/easiest way I could find to implement this. I understand if this way wouldn't be acceptable |
There was a problem hiding this comment.
Pull request overview
This PR adds a new Deserialize method to DatabaseFacade for Cosmos DB that allows deserializing JObject documents into their corresponding entity types. This is particularly useful for scenarios like processing change feed events.
Changes:
- Added two
Deserializeextension methods (generic and non-generic) toCosmosDatabaseFacadeExtensions - Introduced
ICosmosQueryingEnumerable<T>interface to expose the query context and shaper function - Added comprehensive functional tests covering various deserialization scenarios including discriminators and error cases
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
src/EFCore.Cosmos/Extensions/CosmosDatabaseFacadeExtensions.cs |
Implements two Deserialize extension methods with entity type determination logic and shaper invocation |
src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.QueryingEnumerable.cs |
Adds ICosmosQueryingEnumerable<T> interface and implements it in QueryingEnumerable<T> to expose shaper and query context |
test/EFCore.Cosmos.FunctionalTests/Extensions/CosmosDbContextExtensionsDeserializeTest.cs |
Comprehensive test suite covering deserialization scenarios including discriminators, error cases, and change feed processing |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
test/EFCore.Cosmos.FunctionalTests/Extensions/CosmosDbContextExtensionsDeserializeTest.cs
Outdated
Show resolved
Hide resolved
test/EFCore.Cosmos.FunctionalTests/Extensions/CosmosDbContextExtensionsDeserializeTest.cs
Outdated
Show resolved
Hide resolved
| [ConditionalFact] | ||
| public virtual async Task Changefeed() | ||
| { | ||
| using var context = Fixture.CreateContext(); | ||
|
|
||
| var client = context.Database.GetCosmosClient(); | ||
| var container = client.GetContainer( | ||
| context.Database.GetCosmosDatabaseId(), | ||
| context.Model.GetEntityTypes().First(x => x.IsDocumentRoot()).GetContainer()); | ||
|
|
||
| Customer? deserialized = null; | ||
| var processor = container.GetChangeFeedProcessorBuilder("processor", async (IReadOnlyCollection<JObject> changes, CancellationToken ctx) => | ||
| { | ||
| using var context = Fixture.CreateContext(); | ||
| deserialized = context.Database.Deserialize<Customer>(changes.Single()); | ||
| }).WithInMemoryLeaseContainer().Build(); | ||
|
|
||
| await processor.StartAsync(); | ||
| try | ||
| { | ||
| var customer = new Customer { Id = Guid.NewGuid().ToString(), Name = "Customer 1", PartitionKey = "1" }; | ||
| customer.Children.Add(new DummyChild { Id = "1", Name = "Child 1" }); | ||
| context.Customers.Add(customer); | ||
|
|
||
| await context.SaveChangesAsync(); | ||
|
|
||
| var counter = 0; | ||
| while (deserialized == null && counter++ < 30) | ||
| { | ||
| await Task.Delay(1000); | ||
| } | ||
| Assert.NotNull(deserialized); | ||
|
|
||
| Assert.Equal(customer.Name, deserialized.Name); | ||
| Assert.Equal(customer.Id, deserialized.Id); | ||
| } |
There was a problem hiding this comment.
The test creates a Customer entity with owned children but only verifies the parent entity properties after deserialization. The test should also verify that the Children collection is properly populated to ensure owned entities are correctly deserialized in the change feed scenario.
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; |
There was a problem hiding this comment.
The using directive for System is unnecessary and should be removed. The code uses ArgumentNullException.ThrowIfNull and ArgumentException.ThrowIfNullOrWhiteSpace which are available in the global namespace through implicit usings in the project.
| using System; |
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Reflection.Metadata; |
There was a problem hiding this comment.
The using directive for System.Reflection.Metadata is unused and should be removed. No types from this namespace are referenced in the code.
| using System.Reflection.Metadata; |
| using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal; | ||
| using Microsoft.EntityFrameworkCore.Query.Internal; | ||
| using Newtonsoft.Json.Linq; | ||
| using static Microsoft.EntityFrameworkCore.DbLoggerCategory; |
There was a problem hiding this comment.
The static using directive for DbLoggerCategory is unused and should be removed. No members from DbLoggerCategory are referenced in the code.
| using static Microsoft.EntityFrameworkCore.DbLoggerCategory; |
| [ConditionalFact] | ||
| public virtual async Task Deserializes() | ||
| { | ||
| using var context = Fixture.CreateContext(); | ||
|
|
||
| var customer = new Customer { Id = Guid.NewGuid().ToString(), Name = "Customer 1", PartitionKey = "1" }; | ||
| customer.Children.Add(new DummyChild { Id = "1", Name = "Child 1" }); | ||
| context.Customers.Add(customer); | ||
| await context.SaveChangesAsync(); | ||
|
|
||
| var client = context.Database.GetCosmosClient(); | ||
| var container = client.GetContainer( | ||
| context.Database.GetCosmosDatabaseId(), | ||
| context.Model.GetEntityTypes().First(x => x.IsDocumentRoot()).GetContainer()); | ||
|
|
||
| var response = await container.ReadItemAsync<JObject>(customer.Id, new Microsoft.Azure.Cosmos.PartitionKey(customer.PartitionKey)); | ||
| var jObject = response.Resource; | ||
|
|
||
| var deserialized = context.Database.Deserialize<Customer>(jObject); | ||
|
|
||
| Assert.Equal(customer.Name, deserialized.Name); | ||
| Assert.Equal(customer.Id, deserialized.Id); | ||
| } |
There was a problem hiding this comment.
The test creates a Customer entity with owned children but only verifies the parent entity properties after deserialization. The test should also verify that the Children collection is properly populated with the expected DummyChild entity to ensure owned entities are correctly deserialized.
|
Closing for now as per #37542 (comment). |
Implements: #37542