diff --git a/src/EFCore.Cosmos/Extensions/CosmosComplexCollectionBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosComplexCollectionBuilderExtensions.cs
new file mode 100644
index 00000000000..47463f61001
--- /dev/null
+++ b/src/EFCore.Cosmos/Extensions/CosmosComplexCollectionBuilderExtensions.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Cosmos-specific extension methods for .
+///
+///
+/// See Modeling entity types and relationships, and
+/// Accessing Azure Cosmos DB with EF Core for more information and examples.
+///
+public static class CosmosComplexCollectionBuilderExtensions
+{
+ ///
+ /// Configures the property name that the complex collection is mapped to when stored as an embedded document.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The builder for the complex type being configured.
+ /// The name of the parent property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexCollectionBuilder ToJsonProperty(
+ this ComplexCollectionBuilder complexPropertyBuilder,
+ string? name)
+ {
+ complexPropertyBuilder.Metadata.SetJsonPropertyName(name);
+ return complexPropertyBuilder;
+ }
+
+ ///
+ /// Configures the property name that the complex collection is mapped to when stored as an embedded document.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The builder for the complex type being configured.
+ /// The name of the parent property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexCollectionBuilder ToJsonProperty(
+ this ComplexCollectionBuilder complexPropertyBuilder,
+ string? name)
+ where TComplex : notnull
+ {
+ complexPropertyBuilder.Metadata.SetJsonPropertyName(name);
+ return complexPropertyBuilder;
+ }
+}
diff --git a/src/EFCore.Cosmos/Extensions/CosmosComplexCollectionTypePropertyBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosComplexCollectionTypePropertyBuilderExtensions.cs
new file mode 100644
index 00000000000..d1a425ed8ac
--- /dev/null
+++ b/src/EFCore.Cosmos/Extensions/CosmosComplexCollectionTypePropertyBuilderExtensions.cs
@@ -0,0 +1,55 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// ReSharper disable once CheckNamespace
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Cosmos-specific extension methods for .
+///
+///
+/// See Modeling entity types and relationships, and
+/// Accessing Azure Cosmos DB with EF Core for more information and examples.
+///
+public static class CosmosComplexCollectionTypePropertyBuilderExtensions
+{
+ ///
+ /// Configures the property name that the property is mapped to when targeting Azure Cosmos.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The name of the property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexCollectionTypePropertyBuilder ToJsonProperty(
+ this ComplexCollectionTypePropertyBuilder propertyBuilder,
+ string name)
+ {
+ Check.NotEmpty(name);
+
+ propertyBuilder.Metadata.SetJsonPropertyName(name);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the property name that the property is mapped to when targeting Azure Cosmos.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The name of the property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexCollectionTypePropertyBuilder ToJsonProperty(
+ this ComplexCollectionTypePropertyBuilder propertyBuilder,
+ string name)
+ => (ComplexCollectionTypePropertyBuilder)ToJsonProperty((ComplexCollectionTypePropertyBuilder)propertyBuilder, name);
+
+ // Vector and fulltext properties are not supported on collection types
+}
diff --git a/src/EFCore.Cosmos/Extensions/CosmosComplexPropertyBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosComplexPropertyBuilderExtensions.cs
new file mode 100644
index 00000000000..88e795e0795
--- /dev/null
+++ b/src/EFCore.Cosmos/Extensions/CosmosComplexPropertyBuilderExtensions.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Cosmos-specific extension methods for .
+///
+///
+/// See Modeling entity types and relationships, and
+/// Accessing Azure Cosmos DB with EF Core for more information and examples.
+///
+public static class CosmosComplexPropertyBuilderExtensions
+{
+ ///
+ /// Configures the property name that the complex property is mapped to when stored as an embedded document.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The builder for the complex type being configured.
+ /// The name of the parent property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexPropertyBuilder ToJsonProperty(
+ this ComplexPropertyBuilder complexPropertyBuilder,
+ string? name)
+ {
+ complexPropertyBuilder.Metadata.SetJsonPropertyName(name);
+ return complexPropertyBuilder;
+ }
+
+ ///
+ /// Configures the property name that the complex property is mapped to when stored as an embedded document.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The builder for the complex type being configured.
+ /// The name of the parent property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexPropertyBuilder ToJsonProperty(
+ this ComplexPropertyBuilder complexPropertyBuilder,
+ string? name)
+ where TComplex : notnull
+ {
+ complexPropertyBuilder.Metadata.SetJsonPropertyName(name);
+ return complexPropertyBuilder;
+ }
+}
diff --git a/src/EFCore.Cosmos/Extensions/CosmosComplexPropertyExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosComplexPropertyExtensions.cs
new file mode 100644
index 00000000000..a512696d38e
--- /dev/null
+++ b/src/EFCore.Cosmos/Extensions/CosmosComplexPropertyExtensions.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Cosmos.Metadata.Internal;
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Complex property extension methods for Cosmos metadata.
+///
+///
+/// See Modeling entity types and relationships, and
+/// Accessing Azure Cosmos DB with EF Core for more information and examples.
+///
+public static class CosmosComplexPropertyExtensions
+{
+ ///
+ /// Returns the property name that the property is mapped to when targeting Cosmos.
+ ///
+ /// The property.
+ /// Returns the property name that the property is mapped to when targeting Cosmos.
+ public static string GetJsonPropertyName(this IReadOnlyComplexProperty property)
+ => (string?)property[CosmosAnnotationNames.PropertyName]
+ ?? property.Name;
+
+ ///
+ /// Sets the property name that the property is mapped to when targeting Cosmos.
+ ///
+ /// The property.
+ /// The name to set.
+ public static void SetJsonPropertyName(this IMutableComplexProperty property, string? name)
+ => property.SetOrRemoveAnnotation(
+ CosmosAnnotationNames.PropertyName,
+ Check.NullButNotEmpty(name));
+}
diff --git a/src/EFCore.Cosmos/Extensions/CosmosComplexTypePrimitiveCollectionBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosComplexTypePrimitiveCollectionBuilderExtensions.cs
new file mode 100644
index 00000000000..28b34a740ea
--- /dev/null
+++ b/src/EFCore.Cosmos/Extensions/CosmosComplexTypePrimitiveCollectionBuilderExtensions.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Cosmos-specific extension methods for .
+///
+///
+/// See Modeling entity types and relationships, and
+/// Accessing Azure Cosmos DB with EF Core for more information and examples.
+///
+public static class CosmosComplexTypePrimitiveCollectionBuilderExtensions
+{
+ ///
+ /// Configures the property name that the property is mapped to when targeting Azure Cosmos.
+ ///
+ ///
+ ///
+ /// If an empty string is supplied, the property will not be persisted.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ ///
+ /// The builder for the property being configured.
+ /// The name of the property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePrimitiveCollectionBuilder ToJsonProperty(
+ this ComplexTypePrimitiveCollectionBuilder primitiveCollectionBuilder,
+ string name)
+ {
+ Check.NotNull(name);
+
+ primitiveCollectionBuilder.Metadata.SetJsonPropertyName(name);
+
+ return primitiveCollectionBuilder;
+ }
+
+ ///
+ /// Configures the property name that the property is mapped to when targeting Azure Cosmos.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The name of the property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePrimitiveCollectionBuilder ToJsonProperty(
+ this ComplexTypePrimitiveCollectionBuilder primitiveCollectionBuilder,
+ string name)
+ => (ComplexTypePrimitiveCollectionBuilder)ToJsonProperty((ComplexTypePrimitiveCollectionBuilder)primitiveCollectionBuilder, name);
+}
diff --git a/src/EFCore.Cosmos/Extensions/CosmosComplexTypePropertyBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosComplexTypePropertyBuilderExtensions.cs
new file mode 100644
index 00000000000..65176fdb854
--- /dev/null
+++ b/src/EFCore.Cosmos/Extensions/CosmosComplexTypePropertyBuilderExtensions.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// ReSharper disable once CheckNamespace
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Cosmos-specific extension methods for .
+///
+///
+/// See Modeling entity types and relationships, and
+/// Accessing Azure Cosmos DB with EF Core for more information and examples.
+///
+public static class CosmosComplexTypePropertyBuilderExtensions
+{
+ ///
+ /// Configures the property name that the property is mapped to when targeting Azure Cosmos.
+ ///
+ ///
+ ///
+ /// If an empty string is supplied, the property will not be persisted.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ ///
+ /// The builder for the property being configured.
+ /// The name of the property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder ToJsonProperty(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string name)
+ {
+ Check.NotNull(name);
+
+ propertyBuilder.Metadata.SetJsonPropertyName(name);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the property name that the property is mapped to when targeting Azure Cosmos.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The name of the property.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder ToJsonProperty(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string name)
+ => (ComplexTypePropertyBuilder)ToJsonProperty((ComplexTypePropertyBuilder)propertyBuilder, name);
+}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosSerializationUtilities.cs b/src/EFCore.Cosmos/Query/Internal/CosmosSerializationUtilities.cs
index 3bb327b2ed9..8fdc08a938f 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosSerializationUtilities.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosSerializationUtilities.cs
@@ -56,6 +56,11 @@ public static JToken SerializeObjectToComplexProperty(IComplexType type, object?
{
var jsonPropertyName = property.GetJsonPropertyName();
+ if (string.IsNullOrEmpty(jsonPropertyName))
+ {
+ continue;
+ }
+
var propertyValue = property.GetGetter().GetClrValue(value);
#pragma warning disable EF1001 // Internal EF Core API usage.
var providerValue = property.ConvertToProviderValue(propertyValue);
@@ -77,7 +82,7 @@ public static JToken SerializeObjectToComplexProperty(IComplexType type, object?
foreach (var complexProperty in type.GetComplexProperties())
{
- var jsonPropertyName = complexProperty.Name;
+ var jsonPropertyName = complexProperty.GetJsonPropertyName();
var propertyValue = complexProperty.GetGetter().GetClrValue(value);
if (propertyValue is null)
{
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs
index b727e42d12f..99519b67381 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs
@@ -198,7 +198,7 @@ private BlockExpression CreateComplexPropertyAssignmentBlock(MemberExpression me
Call(
CosmosProjectionBindingRemovingExpressionVisitorBase.ToObjectWithSerializerMethodInfo.MakeGenericMethod(typeof(JObject)),
Call(_parentJObject, CosmosProjectionBindingRemovingExpressionVisitorBase.GetItemMethodInfo,
- Constant(complexProperty.Name))));
+ Constant(complexProperty.GetJsonPropertyName()))));
var materializeExpression = CreateComplexTypeMaterializeExpression(complexProperty, jObjectVariable);
if (complexProperty.IsNullable)
@@ -227,7 +227,7 @@ private BlockExpression CreateComplexCollectionAssignmentBlock(MemberExpression
Call(
CosmosProjectionBindingRemovingExpressionVisitorBase.ToObjectWithSerializerMethodInfo.MakeGenericMethod(typeof(JArray)),
Call(_parentJObject, CosmosProjectionBindingRemovingExpressionVisitorBase.GetItemMethodInfo,
- Constant(complexProperty.Name))));
+ Constant(complexProperty.GetJsonPropertyName()))));
var jObjectParameter = Parameter(typeof(JObject), "complexJObject" + _currentComplexIndex);
var materializeExpression = CreateComplexTypeMaterializeExpression(complexProperty, jObjectParameter);
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectAccessExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectAccessExpression.cs
index fedc94aeb0f..b437c8ce6f1 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectAccessExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectAccessExpression.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
-using Microsoft.EntityFrameworkCore.Metadata.Internal;
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
@@ -42,7 +41,7 @@ public ObjectAccessExpression(
break;
case IComplexProperty complexProperty:
structuralType = complexProperty.ComplexType;
- propertyName = complexProperty.Name;
+ propertyName = complexProperty.GetJsonPropertyName();
break;
default:
throw new UnreachableException($"Unexpected structural property type: {structuralProperty.GetType().FullName}");
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayAccessExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayAccessExpression.cs
index dbfb1f8f855..8f12f964f98 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayAccessExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayAccessExpression.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
-using Microsoft.EntityFrameworkCore.Metadata.Internal;
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
@@ -44,7 +43,7 @@ public ObjectArrayAccessExpression(
break;
case IComplexProperty complexProperty:
targetType = complexProperty.ComplexType;
- propertyName = complexProperty.Name;
+ propertyName = complexProperty.GetJsonPropertyName();
break;
default:
throw new UnreachableException($"Unexpected structural property type: {structuralProperty.GetType().FullName}");
diff --git a/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs b/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs
index 93c3169b396..2f5f1cfdd6d 100644
--- a/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs
+++ b/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs
@@ -147,7 +147,7 @@ private JObject CreateDocument(IInternalEntry entry, ITypeBase structuralType, i
foreach (var complexProperty in structuralType.GetComplexProperties())
{
var embeddedValue = entry.GetCurrentValue(complexProperty);
- var embeddedPropertyName = complexProperty.Name;
+ var embeddedPropertyName = complexProperty.GetJsonPropertyName();
if (embeddedValue == null)
{
document[embeddedPropertyName] = null;
@@ -287,7 +287,7 @@ private JObject CreateDocument(IInternalEntry entry, ITypeBase structuralType, i
foreach (var complexProperty in structuralType.GetComplexProperties())
{
var embeddedValue = entry.GetCurrentValue(complexProperty);
- var embeddedPropertyName = complexProperty.Name;
+ var embeddedPropertyName = complexProperty.GetJsonPropertyName();
if (embeddedValue == null)
{
if (document[embeddedPropertyName] != null)
diff --git a/test/EFCore.Cosmos.FunctionalTests/CosmosApiConsistencyTest.cs b/test/EFCore.Cosmos.FunctionalTests/CosmosApiConsistencyTest.cs
index 10586e1f24e..2a9c572bf60 100644
--- a/test/EFCore.Cosmos.FunctionalTests/CosmosApiConsistencyTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/CosmosApiConsistencyTest.cs
@@ -20,6 +20,11 @@ public class CosmosApiConsistencyFixture : ApiConsistencyFixtureBase
{
public override HashSet FluentApiTypes { get; } =
[
+ typeof(CosmosComplexCollectionBuilderExtensions),
+ typeof(CosmosComplexCollectionTypePropertyBuilderExtensions),
+ typeof(CosmosComplexPropertyBuilderExtensions),
+ typeof(CosmosComplexTypePrimitiveCollectionBuilderExtensions),
+ typeof(CosmosComplexTypePropertyBuilderExtensions),
typeof(CosmosPrimitiveCollectionBuilderExtensions),
typeof(CosmosModelBuilderExtensions),
typeof(CosmosPropertyBuilderExtensions),
@@ -65,7 +70,16 @@ public override
typeof(CosmosPropertyBuilderExtensions),
null
)
- }
+ },
+ {
+ typeof(IReadOnlyComplexProperty), (
+ typeof(CosmosComplexPropertyExtensions),
+ null,
+ null,
+ typeof(CosmosComplexPropertyBuilderExtensions),
+ null
+ )
+ },
};
}
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/EmbeddedDocumentsTest.cs b/test/EFCore.Cosmos.FunctionalTests/EmbeddedDocumentsTest.cs
index 153fdb7343f..d552709df32 100644
--- a/test/EFCore.Cosmos.FunctionalTests/EmbeddedDocumentsTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/EmbeddedDocumentsTest.cs
@@ -663,6 +663,108 @@ public virtual async Task Can_change_principal_instance_non_derived()
}
}
+ [ConditionalFact]
+ public virtual async Task Can_use_non_persisted_properties_owned()
+ {
+ var options = await Fixture.CreateOptions(
+ modelBuilder =>
+ {
+ modelBuilder.Entity(eb => eb.OwnsOne(
+ v => v.Operator, b =>
+ {
+ b.Property(x => x.Name).ToJsonProperty("");
+ }));
+ },
+ seed: false);
+
+ using (var context = new EmbeddedTransportationContext(options))
+ {
+ var vehicle = new Vehicle
+ {
+ Name = "Test Vehicle",
+ Operator = new Operator { Name = "Test Operator" }
+ };
+ await context.AddAsync(vehicle);
+ await context.SaveChangesAsync();
+
+ Assert.Equal("Test Operator", vehicle.Operator.Name);
+ }
+
+ using (var context = new EmbeddedTransportationContext(options))
+ {
+ var vehicle = await context.Vehicles.SingleAsync();
+ Assert.Null(vehicle.Operator.Name);
+
+ vehicle.Operator.Name = "Theon Greyjoy";
+ // Assert.Equal(0, await context.SaveChangesAsync()); // @TODO: #37929 non-persisted property changes should not cause SaveChanges to update the document
+ await context.SaveChangesAsync();
+ }
+
+ using (var context = new EmbeddedTransportationContext(options))
+ {
+ var vehicle = await context.Vehicles.SingleAsync();
+ Assert.Null(vehicle.Operator.Name);
+ }
+ }
+
+ [ConditionalFact]
+ public virtual async Task Can_use_non_persisted_properties_complex()
+ {
+ var options = await Fixture.CreateOptions(
+
+ modelBuilder =>
+ {
+ modelBuilder.Entity(eb =>
+ {
+ eb.Ignore(x => x.Operator);
+ eb.ComplexProperty(
+ v => v.Operator, b =>
+ {
+ b.Property(x => x.Name).ToJsonProperty("");
+ });
+ });
+ },
+ seed: false);
+
+ using (var context = new EmbeddedTransportationContext(options))
+ {
+ var vehicle = new Vehicle
+ {
+ Name = "Test Vehicle",
+ Operator = new Operator { Name = "Test Operator" }
+ };
+ await context.AddAsync(vehicle);
+ await context.SaveChangesAsync();
+
+ Assert.Equal("Test Operator", vehicle.Operator.Name);
+ }
+
+ using (var context = new EmbeddedTransportationContext(options))
+ {
+ var vehicle = await context.Vehicles.SingleAsync(x => x.Operator == new Operator());
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["$type"] IN ("Vehicle", "PoweredVehicle") AND (c["Operator"] = {"VehicleName":null,"Details":null}))
+OFFSET 0 LIMIT 2
+""");
+
+ Assert.Null(vehicle.Operator.Name);
+
+ vehicle.Operator.Name = "Theon Greyjoy";
+ // Assert.Equal(0, await context.SaveChangesAsync()); // @TODO: #37929 non-persisted property changes should not cause SaveChanges to update the document
+ await context.SaveChangesAsync();
+ }
+
+ using (var context = new EmbeddedTransportationContext(options))
+ {
+ var vehicle = await context.Vehicles.SingleAsync(x => x.Operator == new Operator());
+ Assert.Null(vehicle.Operator.Name);
+ }
+ }
+
protected TestSqlLoggerFactory TestSqlLoggerFactory
=> (TestSqlLoggerFactory)Fixture.ListLoggerFactory;
@@ -702,7 +804,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
=> ((EmbeddedTransportationContext)context).Options.OnModelCreating?.Invoke(modelBuilder);
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
- => base.AddOptions(builder).ConfigureWarnings(w => w.Ignore(CosmosEventId.NoPartitionKeyDefined));
+ => base.AddOptions(builder).ConfigureWarnings(w => w.Ignore(CosmosEventId.NoPartitionKeyDefined).Ignore(CoreEventId.MappedNavigationIgnoredWarning));
protected override object GetAdditionalModelCacheKey(DbContext context)
{
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/ComplexTypeToJsonPropertyQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/ComplexTypeToJsonPropertyQueryCosmosTest.cs
new file mode 100644
index 00000000000..77e6196f39e
--- /dev/null
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/ComplexTypeToJsonPropertyQueryCosmosTest.cs
@@ -0,0 +1,479 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Cosmos.Internal;
+using Microsoft.EntityFrameworkCore.TestModels.ComplexTypeModel;
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+public class ComplexTypeToJsonPropertyQueryCosmosTest(ComplexTypeToJsonPropertyQueryCosmosTest.ComplexTypeToJsonPropertyQueryCosmosFixture fixture) : ComplexTypeQueryTestBase(fixture), IClassFixture
+{
+ public override Task Filter_on_property_inside_complex_type_after_subquery(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Filter_on_property_inside_complex_type_after_subquery(async), CosmosStrings.LimitOffsetNotSupportedInSubqueries);
+
+ public override Task Filter_on_property_inside_nested_complex_type_after_subquery(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Filter_on_property_inside_nested_complex_type_after_subquery(async), CosmosStrings.LimitOffsetNotSupportedInSubqueries);
+
+ public override Task Filter_on_required_property_inside_required_complex_type_on_optional_navigation(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Filter_on_required_property_inside_required_complex_type_on_optional_navigation(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(Customer), nameof(CustomerGroup)));
+
+ public override Task Filter_on_required_property_inside_required_complex_type_on_required_navigation(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Filter_on_required_property_inside_required_complex_type_on_required_navigation(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(Customer), nameof(CustomerGroup)));
+
+ public override Task Project_complex_type_via_optional_navigation(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_complex_type_via_optional_navigation(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(Customer), nameof(CustomerGroup)));
+
+ public override Task Project_complex_type_via_required_navigation(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_complex_type_via_required_navigation(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(Customer), nameof(CustomerGroup)));
+
+ public override Task Load_complex_type_after_subquery_on_entity_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Load_complex_type_after_subquery_on_entity_type(async), CosmosStrings.LimitOffsetNotSupportedInSubqueries);
+
+ public override Task Select_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Select_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+""");
+ });
+
+ public override Task Select_nested_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Select_nested_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+""");
+ });
+
+ public override Task Select_single_property_on_nested_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Select_single_property_on_nested_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c["ShippingAddressRenamed"]["CountryRenamed"]["FullNameRenamed"]
+FROM root c
+""");
+ });
+
+ public override Task Select_complex_type_Where(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Select_complex_type_Where(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"]["ZipCodeRenamed"] = 7728)
+""");
+ });
+
+ public override async Task Select_complex_type_Distinct(bool async)
+ => await AssertTranslationFailed(async () => await base.Select_complex_type_Distinct(async)); // Cosmos: Projecting out nested documents retrieves the entire document #34067
+
+ public override Task Complex_type_equals_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Complex_type_equals_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"] = c["BillingAddressRenamed"])
+""");
+ });
+
+ public override Task Complex_type_equals_constant(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Complex_type_equals_constant(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"] = {"AddressLine1Renamed":"804 S. Lakeshore Road","AddressLine2Renamed":null,"TagsRenamed":["foo","bar"],"ZipCodeRenamed":38654,"CountryRenamed":{"CodeRenamed":"US","FullNameRenamed":"United States"}})
+""");
+ });
+
+ public override Task Complex_type_equals_parameter(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Complex_type_equals_parameter(async);
+
+ AssertSql(
+ """
+@entity_equality_address='{"AddressLine1Renamed":"804 S. Lakeshore Road","AddressLine2Renamed":null,"TagsRenamed":["foo","bar"],"ZipCodeRenamed":38654,"CountryRenamed":{"CodeRenamed":"US","FullNameRenamed":"United States"}}'
+
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"] = @entity_equality_address)
+""");
+ });
+
+ public override Task Subquery_over_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Subquery_over_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Contains_over_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Contains_over_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Concat_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Concat_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Concat_entity_type_containing_complex_property(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Concat_entity_type_containing_complex_property(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_entity_type_containing_complex_property(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_entity_type_containing_complex_property(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Concat_property_in_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Concat_property_in_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_property_in_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_property_in_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Concat_two_different_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Concat_two_different_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_two_different_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_two_different_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Filter_on_property_inside_struct_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Filter_on_property_inside_struct_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"]["ZipCodeRenamed"] = 7728)
+""");
+ });
+
+ public override Task Filter_on_property_inside_nested_struct_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Filter_on_property_inside_nested_struct_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"]["CountryRenamed"]["CodeRenamed"] = "DE")
+""");
+ });
+
+ public override Task Filter_on_property_inside_struct_complex_type_after_subquery(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Filter_on_property_inside_struct_complex_type_after_subquery(async), CosmosStrings.LimitOffsetNotSupportedInSubqueries);
+
+ public override Task Filter_on_property_inside_nested_struct_complex_type_after_subquery(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Filter_on_property_inside_nested_struct_complex_type_after_subquery(async), CosmosStrings.LimitOffsetNotSupportedInSubqueries);
+
+ public override Task Filter_on_required_property_inside_required_struct_complex_type_on_optional_navigation(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Filter_on_required_property_inside_required_struct_complex_type_on_optional_navigation(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(ValuedCustomer), nameof(ValuedCustomerGroup)));
+
+ public override Task Filter_on_required_property_inside_required_struct_complex_type_on_required_navigation(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Filter_on_required_property_inside_required_struct_complex_type_on_required_navigation(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(ValuedCustomer), nameof(ValuedCustomerGroup)));
+
+ public override Task Project_struct_complex_type_via_optional_navigation(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_struct_complex_type_via_optional_navigation(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(ValuedCustomer), nameof(ValuedCustomerGroup)));
+
+ public override Task Project_nullable_struct_complex_type_via_optional_navigation(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_nullable_struct_complex_type_via_optional_navigation(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(ValuedCustomer), nameof(ValuedCustomerGroup)));
+
+ public override Task Project_struct_complex_type_via_required_navigation(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_struct_complex_type_via_required_navigation(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(ValuedCustomer), nameof(ValuedCustomerGroup)));
+
+ public override Task Load_struct_complex_type_after_subquery_on_entity_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Load_struct_complex_type_after_subquery_on_entity_type(async), CosmosStrings.LimitOffsetNotSupportedInSubqueries);
+
+ public override Task Select_struct_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Select_struct_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+""");
+ });
+
+ public override Task Select_nested_struct_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Select_nested_struct_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+""");
+ });
+
+ public override Task Select_single_property_on_nested_struct_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Select_single_property_on_nested_struct_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c["ShippingAddressRenamed"]["CountryRenamed"]["FullNameRenamed"]
+FROM root c
+""");
+ });
+
+ public override Task Select_struct_complex_type_Where(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Select_struct_complex_type_Where(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"]["ZipCodeRenamed"] = 7728)
+""");
+ });
+
+ public override Task Select_struct_complex_type_Distinct(bool async)
+ => AssertTranslationFailed(() => base.Select_struct_complex_type_Distinct(async)); // #34067
+
+ public override Task Struct_complex_type_equals_struct_complex_type(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Struct_complex_type_equals_struct_complex_type(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"] = c["BillingAddressRenamed"])
+""");
+ });
+
+ public override Task Struct_complex_type_equals_constant(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Struct_complex_type_equals_constant(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"] = {"AddressLine1Renamed":"804 S. Lakeshore Road","AddressLine2Renamed":null,"ZipCodeRenamed":38654,"CountryRenamed":{"CodeRenamed":"US","FullNameRenamed":"United States"}})
+""");
+ });
+
+ public override Task Struct_complex_type_equals_parameter(bool async)
+ => CosmosTestHelpers.Instance.NoSyncTest(async, async (async) =>
+ {
+ await base.Struct_complex_type_equals_parameter(async);
+
+ AssertSql(
+ """
+@entity_equality_address='{"AddressLine1Renamed":"804 S. Lakeshore Road","AddressLine2Renamed":null,"ZipCodeRenamed":38654,"CountryRenamed":{"CodeRenamed":"US","FullNameRenamed":"United States"}}'
+
+SELECT VALUE c
+FROM root c
+WHERE (c["ShippingAddressRenamed"] = @entity_equality_address)
+""");
+ });
+
+ public override Task Subquery_over_struct_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Subquery_over_struct_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Contains_over_struct_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Contains_over_struct_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Concat_struct_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Concat_struct_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Concat_entity_type_containing_struct_complex_property(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Concat_entity_type_containing_struct_complex_property(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_entity_type_containing_struct_complex_property(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_entity_type_containing_struct_complex_property(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_struct_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_struct_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Concat_property_in_struct_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Concat_property_in_struct_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_property_in_struct_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_property_in_struct_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Concat_two_different_struct_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Concat_two_different_struct_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_two_different_struct_complex_type(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_two_different_struct_complex_type(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Project_same_entity_with_nested_complex_type_twice_with_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_same_entity_with_nested_complex_type_twice_with_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Project_same_nested_complex_type_twice_with_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_same_nested_complex_type_twice_with_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Project_same_entity_with_nested_complex_type_twice_with_double_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_same_entity_with_nested_complex_type_twice_with_double_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Project_same_nested_complex_type_twice_with_double_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_same_nested_complex_type_twice_with_double_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Project_same_entity_with_struct_nested_complex_type_twice_with_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_same_entity_with_struct_nested_complex_type_twice_with_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Project_same_struct_nested_complex_type_twice_with_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_same_struct_nested_complex_type_twice_with_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Project_same_entity_with_struct_nested_complex_type_twice_with_double_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_same_entity_with_struct_nested_complex_type_twice_with_double_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Project_same_struct_nested_complex_type_twice_with_double_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_same_struct_nested_complex_type_twice_with_double_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_of_same_entity_with_nested_complex_type_projected_twice_with_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_double_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_of_same_entity_with_nested_complex_type_projected_twice_with_double_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_of_same_nested_complex_type_projected_twice_with_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_of_same_nested_complex_type_projected_twice_with_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Union_of_same_nested_complex_type_projected_twice_with_double_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Union_of_same_nested_complex_type_projected_twice_with_double_pushdown(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+ public override Task Same_entity_with_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(bool async)
+ => AssertTranslationFailed(() => base.Same_entity_with_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(async));
+
+ public override Task Same_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Same_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(async), CosmosStrings.NonCorrelatedSubqueriesNotSupported);
+
+
+ #region GroupBy
+
+ [ConditionalTheory(Skip = "#17313 Cosmos: Translate GroupBy")]
+ public override async Task GroupBy_over_property_in_nested_complex_type(bool async)
+ {
+ await base.GroupBy_over_property_in_nested_complex_type(async);
+
+ AssertSql(
+ """
+
+""");
+ }
+
+ [ConditionalTheory(Skip = "#17313 Cosmos: Translate GroupBy")]
+ public override async Task GroupBy_over_complex_type(bool async)
+ {
+ await base.GroupBy_over_complex_type(async);
+
+ AssertSql(
+ """
+
+""");
+ }
+
+ [ConditionalTheory(Skip = "#17313 Cosmos: Translate GroupBy")]
+ public override async Task GroupBy_over_nested_complex_type(bool async)
+ {
+ await base.GroupBy_over_nested_complex_type(async);
+
+ AssertSql(
+ """
+
+""");
+ }
+
+ [ConditionalTheory(Skip = "#17313 Cosmos: Translate GroupBy")]
+ public override async Task Entity_with_complex_type_with_group_by_and_first(bool async)
+ {
+ await base.Entity_with_complex_type_with_group_by_and_first(async);
+
+ AssertSql(
+ """
+
+""");
+ }
+
+ #endregion GroupBy
+
+ public override Task Projecting_property_of_complex_type_using_left_join_with_pushdown(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Projecting_property_of_complex_type_using_left_join_with_pushdown(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(Customer), nameof(CustomerGroup)));
+
+ public override Task Projecting_complex_from_optional_navigation_using_conditional(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Projecting_complex_from_optional_navigation_using_conditional(async), CosmosStrings.MultipleRootEntityTypesReferencedInQuery(nameof(Customer), nameof(CustomerGroup)));
+
+ public override Task Project_entity_with_complex_type_pushdown_and_then_left_join(bool async)
+ => AssertTranslationFailedWithDetails(() => base.Project_entity_with_complex_type_pushdown_and_then_left_join(async), CosmosStrings.LimitOffsetNotSupportedInSubqueries);
+
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => TestHelpers.AssertAllMethodsOverridden(GetType());
+
+ private void AssertSql(params string[] expected)
+ => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
+
+ public class ComplexTypeToJsonPropertyQueryCosmosFixture : ComplexTypeQueryFixtureBase
+ {
+ protected override string StoreName => nameof(ComplexTypeToJsonPropertyQueryCosmosTest);
+
+ public TestSqlLoggerFactory TestSqlLoggerFactory
+ => (TestSqlLoggerFactory)ListLoggerFactory;
+
+ protected override ITestStoreFactory TestStoreFactory
+ => CosmosTestStoreFactory.Instance;
+
+ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
+ => base.AddOptions(builder)
+ .ConfigureWarnings(w => w.Ignore(CosmosEventId.NoPartitionKeyDefined).Ignore(CoreEventId.MappedEntityTypeIgnoredWarning));
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
+ {
+ base.OnModelCreating(modelBuilder, context);
+ modelBuilder.Entity().ToContainer("Customers");
+ modelBuilder.Entity().ToContainer("CustomerGroups");
+ modelBuilder.Entity().ToContainer("ValuedCustomers");
+ modelBuilder.Entity().ToContainer("ValuedCustomerGroups");
+ foreach (var entityType in modelBuilder.Model.GetEntityTypes())
+ {
+ RenameProperties(entityType);
+ }
+ }
+
+ private void RenameProperties(IMutableTypeBase typeBase)
+ {
+ foreach (var property in typeBase.GetProperties())
+ {
+ if (property.GetJsonPropertyName() == "id")
+ {
+ continue;
+ }
+ property.SetJsonPropertyName(property.Name + "Renamed");
+ }
+ foreach (var property in typeBase.GetComplexProperties())
+ {
+ property.SetJsonPropertyName(property.Name + "Renamed");
+ RenameProperties(property.ComplexType);
+ }
+ }
+ }
+}