diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosKeyDiscoveryConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosKeyDiscoveryConvention.cs index a4e992d6800..c88e74917b7 100644 --- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosKeyDiscoveryConvention.cs +++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosKeyDiscoveryConvention.cs @@ -65,5 +65,7 @@ protected override void ProcessKeyProperties(IList keyPrope keyProperties.Add(partitionKeyProperty); } } + + base.ProcessKeyProperties(keyProperties, entityType); } } diff --git a/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs index 14729e821a8..f1bbd2303e9 100644 --- a/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs @@ -18,6 +18,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// is configured using the foreign key properties with an extra property that matches the naming convention above. /// /// +/// If the entity type is a many-to-many join entity type then the many-to-many foreign key properties are used. +/// +/// /// See Model building conventions for more information and examples. /// /// @@ -57,17 +60,43 @@ public KeyDiscoveryConvention(ProviderConventionSetBuilderDependencies dependenc protected virtual void TryConfigurePrimaryKey(IConventionEntityTypeBuilder entityTypeBuilder) { var entityType = entityTypeBuilder.Metadata; - if (entityType.BaseType != null - || (entityType.IsKeyless && entityType.GetIsKeylessConfigurationSource() != ConfigurationSource.Convention) - || !entityTypeBuilder.CanSetPrimaryKey((IReadOnlyList?)null)) + if (!ShouldDiscoverKeyProperties(entityType)) { return; } + var keyProperties = DiscoverKeyProperties(entityType); + if (keyProperties != null) + { + ProcessKeyProperties(keyProperties, entityType); + + if (keyProperties.Count > 0) + { + entityTypeBuilder.PrimaryKey(keyProperties); + } + } + } + + /// + /// Determines whether key properties should be discovered for the entity type. + /// + /// The entity type. + /// if key properties should be discovered, otherwise . + protected virtual bool ShouldDiscoverKeyProperties(IConventionEntityType entityType) => + entityType.BaseType == null + && (!entityType.IsKeyless || entityType.GetIsKeylessConfigurationSource() == ConfigurationSource.Convention) + && entityType.Builder.CanSetPrimaryKey((IReadOnlyList?)null); + + /// + /// Returns the properties that should be used for the primary key. + /// + /// The entity type. + /// The properties that should be used for the primary key. + protected virtual List? DiscoverKeyProperties(IConventionEntityType entityType) + { List? keyProperties = null; var ownership = entityType.FindOwnership(); - if (ownership != null - && ownership.DeclaringEntityType != entityType) + if (ownership?.DeclaringEntityType != entityType) { ownership = null; } @@ -86,7 +115,7 @@ protected virtual void TryConfigurePrimaryKey(IConventionEntityTypeBuilder entit if (keyProperties.Count > 1) { Dependencies.Logger.MultiplePrimaryKeyCandidates(keyProperties[0], keyProperties[1]); - return; + return null; } } @@ -101,7 +130,7 @@ protected virtual void TryConfigurePrimaryKey(IConventionEntityTypeBuilder entit || primaryKey!.Properties.Count == 1 || ownership.Properties.Contains(shadowProperty)) { - shadowProperty = entityTypeBuilder.CreateUniqueProperty(typeof(int), "Id", required: true)!.Metadata; + shadowProperty = entityType.Builder.CreateUniqueProperty(typeof(int), "Id", required: true)!.Metadata; } keyProperties.Clear(); @@ -125,6 +154,19 @@ protected virtual void TryConfigurePrimaryKey(IConventionEntityTypeBuilder entit } } + return keyProperties; + } + + /// + /// Adds or removes properties to be used for the primary key. + /// + /// The properties that will be used to configure the key. + /// The entity type being configured. + protected virtual void ProcessKeyProperties( + IList keyProperties, + IConventionEntityType entityType) + { + // Remove duplicates for (var i = keyProperties.Count - 1; i >= 0; i--) { var property = keyProperties[i]; @@ -137,24 +179,6 @@ protected virtual void TryConfigurePrimaryKey(IConventionEntityTypeBuilder entit } } } - - ProcessKeyProperties(keyProperties, entityType); - - if (keyProperties.Count > 0) - { - entityTypeBuilder.PrimaryKey(keyProperties); - } - } - - /// - /// Adds or removes properties to be used for the primary key. - /// - /// The properties that will be used to configure the key. - /// The entity type being configured. - protected virtual void ProcessKeyProperties( - IList keyProperties, - IConventionEntityType entityType) - { } /// @@ -179,9 +203,9 @@ public static IEnumerable DiscoverKeyProperties( && p.Name.StartsWith(entityTypeName, StringComparison.OrdinalIgnoreCase) && p.Name.EndsWith(KeySuffix, StringComparison.OrdinalIgnoreCase)); } + // ReSharper restore PossibleMultipleEnumeration return keyProperties; - // ReSharper restore PossibleMultipleEnumeration } /// diff --git a/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs index ba36e5ca5e1..2da7748a793 100644 --- a/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs @@ -12,323 +12,319 @@ public class JsonTypesCosmosTest : JsonTypesTestBase // #25765 - the Cosmos type mapping source doesn't support primitive collections, so we end up with a Property // that has no ElementType; that causes the assertion on the element nullability to fail. public override void Can_read_write_collection_of_string_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_string_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_string_JSON_values); // #25765 - the Cosmos type mapping source doesn't support primitive collections, so we end up with a Property // that has no ElementType; that causes the assertion on the element nullability to fail. public override void Can_read_write_collection_of_binary_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_binary_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_binary_JSON_values); // #25765 - the Cosmos type mapping source doesn't support primitive collections, so we end up with a Property // that has no ElementType; that causes the assertion on the element nullability to fail. public override void Can_read_write_collection_of_nullable_string_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_string_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_string_JSON_values); public override void Can_read_write_binary_as_collection() - => Assert.Throws(() => base.Can_read_write_binary_as_collection()); + => Assert.Throws(base.Can_read_write_binary_as_collection); public override void Can_read_write_collection_of_bool_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_bool_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_bool_JSON_values); public override void Can_read_write_collection_of_byte_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_byte_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_byte_enum_JSON_values); public override void Can_read_write_collection_of_byte_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_byte_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_byte_JSON_values); public override void Can_read_write_collection_of_char_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_char_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_char_JSON_values); public override void Can_read_write_collection_of_DateOnly_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_DateOnly_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_DateOnly_JSON_values); public override void Can_read_write_collection_of_DateTime_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_DateTime_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_DateTime_JSON_values); public override void Can_read_write_collection_of_DateTimeOffset_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_DateTimeOffset_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_DateTimeOffset_JSON_values); public override void Can_read_write_collection_of_decimal_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_decimal_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_decimal_JSON_values); public override void Can_read_write_collection_of_decimal_with_precision_and_scale_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_decimal_with_precision_and_scale_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_decimal_with_precision_and_scale_JSON_values); public override void Can_read_write_collection_of_double_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_double_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_double_JSON_values); public override void Can_read_write_collection_of_float_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_float_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_float_JSON_values); public override void Can_read_write_collection_of_Guid_converted_to_bytes_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_Guid_converted_to_bytes_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_Guid_converted_to_bytes_JSON_values); public override void Can_read_write_collection_of_GUID_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_GUID_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_GUID_JSON_values); public override void Can_read_write_collection_of_int_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_int_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_int_enum_JSON_values); public override void Can_read_write_collection_of_int_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_int_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_int_JSON_values); public override void Can_read_write_collection_of_int_with_converter_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_int_with_converter_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_int_with_converter_JSON_values); public override void Can_read_write_collection_of_long_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_long_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_long_enum_JSON_values); public override void Can_read_write_collection_of_long_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_long_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_long_JSON_values); public override void Can_read_write_collection_of_nullable_binary_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_binary_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_binary_JSON_values); public override void Can_read_write_collection_of_nullable_bool_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_bool_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_bool_JSON_values); public override void Can_read_write_collection_of_nullable_byte_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_byte_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_byte_enum_JSON_values); public override void Can_read_write_collection_of_nullable_byte_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_byte_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_byte_JSON_values); public override void Can_read_write_collection_of_nullable_char_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_char_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_char_JSON_values); public override void Can_read_write_collection_of_nullable_DateOnly_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_DateOnly_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_DateOnly_JSON_values); public override void Can_read_write_collection_of_nullable_DateTime_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_DateTime_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_DateTime_JSON_values); public override void Can_read_write_collection_of_nullable_DateTimeOffset_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_DateTimeOffset_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_DateTimeOffset_JSON_values); public override void Can_read_write_collection_of_nullable_decimal_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_decimal_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_decimal_JSON_values); public override void Can_read_write_collection_of_nullable_double_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_double_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_double_JSON_values); public override void Can_read_write_collection_of_nullable_float_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_float_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_float_JSON_values); public override void Can_read_write_collection_of_nullable_GUID_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_GUID_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_GUID_JSON_values); public override void Can_read_write_collection_of_nullable_int_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_int_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_int_enum_JSON_values); public override void Can_read_write_collection_of_nullable_int_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_int_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_int_JSON_values); public override void Can_read_write_collection_of_ushort_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_ushort_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_ushort_JSON_values); public override void Can_read_write_collection_of_ushort_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_ushort_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_ushort_enum_JSON_values); public override void Can_read_write_collection_of_URI_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_URI_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_URI_JSON_values); public override void Can_read_write_collection_of_ulong_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_ulong_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_ulong_JSON_values); public override void Can_read_write_collection_of_ulong_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_ulong_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_ulong_enum_JSON_values); public override void Can_read_write_collection_of_uint_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_uint_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_uint_JSON_values); public override void Can_read_write_collection_of_uint_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_uint_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_uint_enum_JSON_values); public override void Can_read_write_collection_of_TimeSpan_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_TimeSpan_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_TimeSpan_JSON_values); public override void Can_read_write_collection_of_TimeOnly_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_TimeOnly_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_TimeOnly_JSON_values); public override void Can_read_write_collection_of_short_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_short_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_short_JSON_values); public override void Can_read_write_collection_of_short_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_short_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_short_enum_JSON_values); public override void Can_read_write_collection_of_sbyte_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_sbyte_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_sbyte_JSON_values); public override void Can_read_write_collection_of_sbyte_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_sbyte_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_sbyte_enum_JSON_values); public override void Can_read_write_collection_of_physical_address_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_physical_address_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_physical_address_JSON_values); public override void Can_read_write_collection_of_nullable_ushort_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_ushort_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_ushort_JSON_values); public override void Can_read_write_collection_of_nullable_ushort_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_ushort_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_ushort_enum_JSON_values); public override void Can_read_write_collection_of_nullable_URI_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_URI_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_URI_JSON_values); public override void Can_read_write_collection_of_nullable_ulong_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_ulong_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_ulong_JSON_values); public override void Can_read_write_collection_of_nullable_ulong_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_ulong_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_ulong_enum_JSON_values); public override void Can_read_write_collection_of_nullable_uint_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_uint_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_uint_JSON_values); public override void Can_read_write_collection_of_nullable_uint_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_uint_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_uint_enum_JSON_values); public override void Can_read_write_collection_of_nullable_TimeSpan_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_TimeSpan_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_TimeSpan_JSON_values); public override void Can_read_write_collection_of_nullable_TimeOnly_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_TimeOnly_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_TimeOnly_JSON_values); public override void Can_read_write_collection_of_nullable_short_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_short_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_short_JSON_values); public override void Can_read_write_collection_of_nullable_short_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_short_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_short_enum_JSON_values); public override void Can_read_write_collection_of_nullable_sbyte_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_sbyte_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_sbyte_JSON_values); public override void Can_read_write_collection_of_nullable_sbyte_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_sbyte_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_sbyte_enum_JSON_values); public override void Can_read_write_collection_of_nullable_physical_address_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_physical_address_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_physical_address_JSON_values); public override void Can_read_write_collection_of_nullable_long_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_long_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_long_JSON_values); public override void Can_read_write_collection_of_nullable_long_enum_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_long_enum_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_long_enum_JSON_values); public override void Can_read_write_collection_of_nullable_IP_address_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_IP_address_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_IP_address_JSON_values); public override void Can_read_write_collection_of_nullable_int_with_converter_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_nullable_int_with_converter_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_nullable_int_with_converter_JSON_values); public override void Can_read_write_collection_of_IP_address_JSON_values() - => Assert.Throws(() => base.Can_read_write_collection_of_IP_address_JSON_values()); + => Assert.Throws(base.Can_read_write_collection_of_IP_address_JSON_values); public override void Can_read_write_point() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point()); + => Assert.Throws(base.Can_read_write_point); public override void Can_read_write_point_with_Z() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point_with_Z()); + => Assert.Throws(base.Can_read_write_point_with_Z); public override void Can_read_write_point_with_M() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point_with_M()); + => Assert.Throws(base.Can_read_write_point_with_M); public override void Can_read_write_point_with_Z_and_M() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point_with_Z_and_M()); + => Assert.Throws(base.Can_read_write_point_with_Z_and_M); public override void Can_read_write_line_string() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_line_string()); + => Assert.Throws(base.Can_read_write_line_string); public override void Can_read_write_multi_line_string() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_multi_line_string()); + => Assert.Throws(base.Can_read_write_multi_line_string); public override void Can_read_write_polygon() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_polygon()); + => Assert.Throws(base.Can_read_write_polygon); public override void Can_read_write_polygon_typed_as_geometry() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_polygon_typed_as_geometry()); + => Assert.Throws(base.Can_read_write_polygon_typed_as_geometry); public override void Can_read_write_point_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point_as_GeoJson()); + => Assert.Throws(base.Can_read_write_point_as_GeoJson); public override void Can_read_write_point_with_Z_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point_with_Z_as_GeoJson()); + => Assert.Throws(base.Can_read_write_point_with_Z_as_GeoJson); public override void Can_read_write_point_with_M_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point_with_M_as_GeoJson()); + => Assert.Throws(base.Can_read_write_point_with_M_as_GeoJson); public override void Can_read_write_point_with_Z_and_M_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point_with_Z_and_M_as_GeoJson()); + => Assert.Throws(base.Can_read_write_point_with_Z_and_M_as_GeoJson); public override void Can_read_write_line_string_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_line_string_as_GeoJson()); + => Assert.Throws(base.Can_read_write_line_string_as_GeoJson); public override void Can_read_write_multi_line_string_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_multi_line_string_as_GeoJson()); + => Assert.Throws(base.Can_read_write_multi_line_string_as_GeoJson); public override void Can_read_write_polygon_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_polygon_as_GeoJson()); + => Assert.Throws(base.Can_read_write_polygon_as_GeoJson); public override void Can_read_write_polygon_typed_as_geometry_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_polygon_typed_as_geometry_as_GeoJson()); + => Assert.Throws(base.Can_read_write_polygon_typed_as_geometry_as_GeoJson); public override void Can_read_write_nullable_point() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point()); + => Assert.Throws(base.Can_read_write_point); public override void Can_read_write_nullable_line_string() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_line_string()); + => Assert.Throws(base.Can_read_write_line_string); public override void Can_read_write_nullable_multi_line_string() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_multi_line_string()); + => Assert.Throws(base.Can_read_write_multi_line_string); public override void Can_read_write_nullable_polygon() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_polygon()); + => Assert.Throws(base.Can_read_write_polygon); public override void Can_read_write_nullable_point_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_point_as_GeoJson()); + => Assert.Throws(base.Can_read_write_point_as_GeoJson); public override void Can_read_write_nullable_line_string_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_line_string_as_GeoJson()); + => Assert.Throws(base.Can_read_write_line_string_as_GeoJson); public override void Can_read_write_nullable_multi_line_string_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_multi_line_string_as_GeoJson()); + => Assert.Throws(base.Can_read_write_multi_line_string_as_GeoJson); public override void Can_read_write_nullable_polygon_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_polygon_as_GeoJson()); + => Assert.Throws(base.Can_read_write_polygon_as_GeoJson); public override void Can_read_write_polygon_typed_as_nullable_geometry() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_polygon_typed_as_nullable_geometry()); + => Assert.Throws(base.Can_read_write_polygon_typed_as_nullable_geometry); public override void Can_read_write_polygon_typed_as_nullable_geometry_as_GeoJson() // No built-in JSON support for spatial types in the Cosmos provider - => Assert.Throws(() => base.Can_read_write_polygon_typed_as_nullable_geometry_as_GeoJson()); + => Assert.Throws(base.Can_read_write_polygon_typed_as_nullable_geometry_as_GeoJson); - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - var store = CosmosTestStore.GetOrCreate(nameof(JsonTypesCosmosTest)); - base.OnConfiguring(optionsBuilder.UseCosmos(store.ConnectionUri, store.AuthToken, store.Name)); - } + protected override ITestStoreFactory TestStoreFactory => CosmosTestStoreFactory.Instance; } diff --git a/test/EFCore.InMemory.FunctionalTests/JsonTypesInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/JsonTypesInMemoryTest.cs index b5364a02350..998a87bce0b 100644 --- a/test/EFCore.InMemory.FunctionalTests/JsonTypesInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/JsonTypesInMemoryTest.cs @@ -37,8 +37,7 @@ public override void Can_read_write_polygon() public override void Can_read_write_polygon_typed_as_geometry() // No built-in JSON support for spatial types in the in-memory provider - => Assert.Throws(() => base.Can_read_write_polygon_typed_as_geometry()); + => Assert.Throws(base.Can_read_write_polygon_typed_as_geometry); - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => base.OnConfiguring(optionsBuilder.UseInMemoryDatabase("X")); + protected override ITestStoreFactory TestStoreFactory => InMemoryTestStoreFactory.Instance; } diff --git a/test/EFCore.Relational.Specification.Tests/JsonTypesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/JsonTypesRelationalTestBase.cs index 227948d01fd..9ce65deeeea 100644 --- a/test/EFCore.Relational.Specification.Tests/JsonTypesRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/JsonTypesRelationalTestBase.cs @@ -7,7 +7,7 @@ namespace Microsoft.EntityFrameworkCore; -public class JsonTypesRelationalTestBase : JsonTypesTestBase +public abstract class JsonTypesRelationalTestBase : JsonTypesTestBase { [ConditionalTheory] [InlineData(null)] diff --git a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs index d36f85685db..c72a58a54df 100644 --- a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs +++ b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs @@ -19,7 +19,7 @@ namespace Microsoft.EntityFrameworkCore; -public abstract class JsonTypesTestBase +public abstract class JsonTypesTestBase : NonSharedModelTestBase { [ConditionalTheory] [InlineData(sbyte.MinValue, """{"Prop":-128}""")] @@ -3562,7 +3562,12 @@ protected virtual void Can_read_and_write_JSON_value( Dictionary? facets = null) where TEntity : class { - using var context = new SingleTypeDbContext(OnConfiguring, buildModel, configureConventions); + var contextFactory = CreateContextFactory( + buildModel, + addServices: AddServices, + configureConventions: configureConventions); + using var context = contextFactory.CreateContext(); + var property = context.Model.FindEntityType(typeof(TEntity))!.GetProperty(propertyName); using var stream = new MemoryStream(); @@ -3635,6 +3640,11 @@ protected virtual void Can_read_and_write_JSON_value( } } + protected override string StoreName => "JsonTypesTest"; + + protected virtual IServiceCollection AddServices(IServiceCollection serviceCollection) + => serviceCollection; + protected virtual void AssertElementFacets(IElementType element, Dictionary? facets) { Assert.Equal(FacetValue(CoreAnnotationNames.Precision), element.GetPrecision()); @@ -3875,46 +3885,5 @@ public override void ToJsonTyped(Utf8JsonWriter writer, Geometry value) } } - protected class SingleTypeDbContext : DbContext - { - private readonly Action _buildOptions; - private readonly Action? _configureConventions; - private readonly Action _buildModel; - - public SingleTypeDbContext( - Action buildOptions, - Action buildModel, - Action? configureConventions = null) - { - _buildOptions = buildOptions; - _configureConventions = configureConventions; - _buildModel = buildModel; - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => _buildOptions(optionsBuilder.ReplaceService()); - - protected override void OnModelCreating(ModelBuilder modelBuilder) - => _buildModel(modelBuilder); - - protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) - => _configureConventions?.Invoke(configurationBuilder); - - private class DegenerateCacheKeyFactory : IModelCacheKeyFactory - { - private static int _value; - - public object Create(DbContext context, bool designTime) - => _value++; - } - } - - protected virtual void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.ConfigureWarnings( - w => w.Ignore( - CoreEventId.MappedEntityTypeIgnoredWarning, - CoreEventId.MappedPropertyIgnoredWarning, - CoreEventId.MappedNavigationIgnoredWarning)); - private readonly NullabilityInfoContext _nullabilityInfoContext = new(); } diff --git a/test/EFCore.Specification.Tests/NonSharedModelTestBase.cs b/test/EFCore.Specification.Tests/NonSharedModelTestBase.cs index 50f2dc48880..b472cfdfdae 100644 --- a/test/EFCore.Specification.Tests/NonSharedModelTestBase.cs +++ b/test/EFCore.Specification.Tests/NonSharedModelTestBase.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore; public abstract class NonSharedModelTestBase : IDisposable, IAsyncLifetime { - public static IEnumerable IsAsyncData = new[] { new object[] { false }, new object[] { true } }; + public static IEnumerable IsAsyncData = new object[][] { [false], [ true ] }; protected abstract string StoreName { get; } protected abstract ITestStoreFactory TestStoreFactory { get; } @@ -39,6 +39,7 @@ protected virtual ContextFactory Initialize( Action onModelCreating = null, Action onConfiguring = null, Func addServices = null, + Action configureConventions = null, Action seed = null, Func shouldLogCategory = null, Func createTestStore = null, @@ -46,7 +47,7 @@ protected virtual ContextFactory Initialize( where TContext : DbContext { var contextFactory = CreateContextFactory( - onModelCreating, onConfiguring, addServices, shouldLogCategory, createTestStore, usePooling); + onModelCreating, onConfiguring, addServices, configureConventions, shouldLogCategory, createTestStore, usePooling); TestStore.Initialize(_serviceProvider, contextFactory.CreateContext, seed == null ? null : c => seed((TContext)c)); @@ -59,6 +60,7 @@ protected virtual Task> InitializeAsync( Action onModelCreating = null, Action onConfiguring = null, Func addServices = null, + Action configureConventions = null, Action seed = null, Func shouldLogCategory = null, Func createTestStore = null, @@ -66,7 +68,7 @@ protected virtual Task> InitializeAsync( where TContext : DbContext { var contextFactory = CreateContextFactory( - onModelCreating, onConfiguring, addServices, shouldLogCategory, createTestStore, usePooling); + onModelCreating, onConfiguring, addServices, configureConventions, shouldLogCategory, createTestStore, usePooling); TestStore.Initialize(_serviceProvider, contextFactory.CreateContext, seed == null ? null : c => seed((TContext)c)); @@ -79,6 +81,7 @@ protected ContextFactory CreateContextFactory( Action onModelCreating = null, Action onConfiguring = null, Func addServices = null, + Action configureConventions = null, Func shouldLogCategory = null, Func createTestStore = null, bool usePooling = true) @@ -92,7 +95,7 @@ protected ContextFactory CreateContextFactory( if (onModelCreating != null) { - services = services.AddSingleton(TestModelSource.GetFactory(onModelCreating)); + services = services.AddSingleton(TestModelSource.GetFactory(onModelCreating, configureConventions)); } addServices?.Invoke(services); diff --git a/test/EFCore.Specification.Tests/Scaffolding/CompiledModelTestBase.cs b/test/EFCore.Specification.Tests/Scaffolding/CompiledModelTestBase.cs index 38beabae27c..5c1391a12c1 100644 --- a/test/EFCore.Specification.Tests/Scaffolding/CompiledModelTestBase.cs +++ b/test/EFCore.Specification.Tests/Scaffolding/CompiledModelTestBase.cs @@ -1278,7 +1278,7 @@ protected virtual (TContext?, IModel?) Test( }, onConfiguring, addServices); - var context = contextFactory.CreateContext(); + using var context = contextFactory.CreateContext(); var model = context.GetService().Model; options ??= new CompiledModelCodeGenerationOptions(); diff --git a/test/EFCore.SqlServer.FunctionalTests/JsonTypesCustomMappingSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/JsonTypesCustomMappingSqlServerTest.cs index de19e205d18..ac0e2d5c7b7 100644 --- a/test/EFCore.SqlServer.FunctionalTests/JsonTypesCustomMappingSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/JsonTypesCustomMappingSqlServerTest.cs @@ -9,8 +9,8 @@ namespace Microsoft.EntityFrameworkCore; public class JsonTypesCustomMappingSqlServerTest : JsonTypesSqlServerTestBase { - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => base.OnConfiguring(optionsBuilder.ReplaceService()); + protected override IServiceCollection AddServices(IServiceCollection serviceCollection) + => serviceCollection.AddSingleton(); private class TestSqlServerTypeMappingSource( TypeMappingSourceDependencies dependencies, diff --git a/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs b/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs index 27ef33a5951..68a741aec67 100644 --- a/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs +++ b/test/EFCore.SqlServer.FunctionalTests/JsonTypesSqlServerTestBase.cs @@ -62,6 +62,14 @@ public override void Can_read_write_collection_of_fixed_length_string_JSON_value public override void Can_read_write_collection_of_ASCII_string_JSON_values(object? storeType) => base.Can_read_write_collection_of_ASCII_string_JSON_values("varchar(max)"); - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => base.OnConfiguring(optionsBuilder.UseSqlServer(b => b.UseNetTopologySuite())); + + protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; + + protected override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + { + builder = base.AddOptions(builder) + .ConfigureWarnings(w => w.Ignore(SqlServerEventId.DecimalTypeDefaultWarning)); + new SqlServerDbContextOptionsBuilder(builder).UseNetTopologySuite(); + return builder; + } } diff --git a/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs index bdc4b917395..8c61b762988 100644 --- a/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs @@ -11,9 +11,6 @@ namespace Microsoft.EntityFrameworkCore; [SpatialiteRequired] public class JsonTypesSqliteTest : JsonTypesRelationalTestBase { - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => base.OnConfiguring(optionsBuilder.UseSqlite(b => b.UseNetTopologySuite())); - public override void Can_read_write_binary_JSON_values(string value, string json) => base.Can_read_write_binary_JSON_values( value, value switch @@ -338,4 +335,15 @@ public override void Can_read_write_collection_of_nullable_GUID_JSON_values() }, """{"Prop":["00000000-0000-0000-0000-000000000000",null,"8C44242F-8E3F-4A20-8BE8-98C7C1AADEBD","FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"]}""", mappedCollection: true); + + protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance; + protected override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + { + builder = base.AddOptions(builder) + .ConfigureWarnings(w => w + .Ignore(SqliteEventId.SchemaConfiguredWarning) + .Ignore(SqliteEventId.CompositeKeyWithValueGeneration)); + new SqliteDbContextOptionsBuilder(builder).UseNetTopologySuite(); + return builder; + } }