diff --git a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeBuilderExtensions.cs
index b1ef2a7ce2d..24efef0d0fc 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeBuilderExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeBuilderExtensions.cs
@@ -883,88 +883,6 @@ public static bool CanSetDefaultTimeToLive(
bool fromDataAnnotation = false)
=> entityTypeBuilder.CanSetAnnotation(CosmosAnnotationNames.DefaultTimeToLive, seconds, fromDataAnnotation);
- ///
- /// Configures a default language to use for full-text search at container scope.
- ///
- ///
- /// See Modeling entity types and relationships , and
- /// Accessing Azure Cosmos DB with EF Core for more information and examples.
- ///
- /// The builder for the entity type being configured.
- /// The default language.
- /// The same builder instance so that multiple calls can be chained.
- public static EntityTypeBuilder HasDefaultFullTextLanguage(
- this EntityTypeBuilder entityTypeBuilder,
- string? language)
- {
- entityTypeBuilder.Metadata.SetDefaultFullTextSearchLanguage(language);
-
- return entityTypeBuilder;
- }
-
- ///
- /// Configures a default language to use for full-text search at container scope.
- ///
- ///
- /// See Modeling entity types and relationships , and
- /// Accessing Azure Cosmos DB with EF Core for more information and examples.
- ///
- /// The builder for the entity type being configured.
- /// The default language.
- /// The same builder instance so that multiple calls can be chained.
- public static EntityTypeBuilder HasDefaultFullTextLanguage(
- this EntityTypeBuilder entityTypeBuilder,
- string? language)
- where TEntity : class
- => (EntityTypeBuilder)HasDefaultFullTextLanguage((EntityTypeBuilder)entityTypeBuilder, language);
-
- ///
- /// Configures a default language to use for full-text search at container scope.
- ///
- ///
- /// See Modeling entity types and relationships , and
- /// Accessing Azure Cosmos DB with EF Core for more information and examples.
- ///
- /// The builder for the entity type being configured.
- /// The default language.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- public static IConventionEntityTypeBuilder? HasDefaultFullTextLanguage(
- this IConventionEntityTypeBuilder entityTypeBuilder,
- string? language,
- bool fromDataAnnotation = false)
- {
- if (!entityTypeBuilder.CanSetDefaultFullTextLanguage(language, fromDataAnnotation))
- {
- return null;
- }
-
- entityTypeBuilder.Metadata.SetDefaultFullTextSearchLanguage(language, fromDataAnnotation);
-
- return entityTypeBuilder;
- }
-
- ///
- /// Returns a value indicating whether the default full-text language can be set
- /// from the current configuration source
- ///
- ///
- /// See Modeling entity types and relationships , and
- /// Accessing Azure Cosmos DB with EF Core for more information and examples.
- ///
- /// The builder for the entity type being configured.
- /// The default language.
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the configuration can be applied.
- public static bool CanSetDefaultFullTextLanguage(
- this IConventionEntityTypeBuilder entityTypeBuilder,
- string? language,
- bool fromDataAnnotation = false)
- => entityTypeBuilder.CanSetAnnotation(CosmosAnnotationNames.DefaultFullTextSearchLanguage, language, fromDataAnnotation);
-
///
/// Configures the manual provisioned throughput offering.
///
diff --git a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs
index 0ef110abb91..02f86c605e1 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs
@@ -586,48 +586,4 @@ public static void SetThroughput(this IMutableEntityType entityType, int? throug
public static ConfigurationSource? GetThroughputConfigurationSource(this IConventionEntityType entityType)
=> entityType.FindAnnotation(CosmosAnnotationNames.Throughput)
?.GetConfigurationSource();
-
- ///
- /// Returns the default language for the full-text search at container scope.
- ///
- /// The entity type.
- /// The default language for the full-text search.
- public static string? GetDefaultFullTextSearchLanguage(this IReadOnlyEntityType entityType)
- => entityType.BaseType != null
- ? entityType.GetRootType().GetDefaultFullTextSearchLanguage()
- : (string?)entityType[CosmosAnnotationNames.DefaultFullTextSearchLanguage];
-
- ///
- /// Sets the default language for the full-text search at container scope.
- ///
- /// The entity type.
- /// The default language for the full-text search.
- public static void SetDefaultFullTextSearchLanguage(this IMutableEntityType entityType, string? language)
- => entityType.SetOrRemoveAnnotation(
- CosmosAnnotationNames.DefaultFullTextSearchLanguage,
- language);
-
- ///
- /// Sets the default language for the full-text search at container scope.
- ///
- /// The entity type.
- /// The default language for the full-text search.
- /// Indicates whether the configuration was specified using a data annotation.
- public static string? SetDefaultFullTextSearchLanguage(
- this IConventionEntityType entityType,
- string? language,
- bool fromDataAnnotation = false)
- => (string?)entityType.SetOrRemoveAnnotation(
- CosmosAnnotationNames.DefaultFullTextSearchLanguage,
- language,
- fromDataAnnotation)?.Value;
-
- ///
- /// Gets the for the default full-text search language at container scope.
- ///
- /// The entity type to find configuration source for.
- /// The for the default full-text search language.
- public static ConfigurationSource? GetDefaultFullTextSearchLanguageConfigurationSource(this IConventionEntityType entityType)
- => entityType.FindAnnotation(CosmosAnnotationNames.DefaultFullTextSearchLanguage)
- ?.GetConfigurationSource();
}
diff --git a/src/EFCore.Cosmos/Extensions/CosmosIndexBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosIndexBuilderExtensions.cs
index d81309fbdf9..3edbb250a1a 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosIndexBuilderExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosIndexBuilderExtensions.cs
@@ -25,10 +25,14 @@ public static class CosmosIndexBuilderExtensions
///
/// The builder for the index being configured.
/// The type of vector index to create.
+ /// The value indicating whether the index is configured as a vector index.
/// A builder to further configure the index.
- public static IndexBuilder IsVectorIndex(this IndexBuilder indexBuilder, VectorIndexType? indexType)
+ public static IndexBuilder IsVectorIndex(
+ this IndexBuilder indexBuilder,
+ VectorIndexType? indexType,
+ bool vectorIndex = true)
{
- indexBuilder.Metadata.SetVectorIndexType(indexType);
+ indexBuilder.Metadata.SetVectorIndexType(indexType, vectorIndex);
return indexBuilder;
}
@@ -43,10 +47,12 @@ public static IndexBuilder IsVectorIndex(this IndexBuilder indexBuilder, VectorI
///
/// The builder for the index being configured.
/// The type of vector index to create.
+ /// The value indicating whether the index is configured as a vector index.
/// A builder to further configure the index.
public static IndexBuilder IsVectorIndex(
this IndexBuilder indexBuilder,
- VectorIndexType? indexType)
+ VectorIndexType? indexType,
+ bool vectorIndex = true)
=> (IndexBuilder)IsVectorIndex((IndexBuilder)indexBuilder, indexType);
///
@@ -104,11 +110,11 @@ public static bool CanSetVectorIndexType(
/// Accessing Azure Cosmos DB with EF Core for more information and examples.
///
/// The builder for the index being configured.
- /// The value indicating whether the index is configured for Full-text search.
+ /// The value indicating whether the index is configured for Full-text search.
/// A builder to further configure the index.
- public static IndexBuilder IsFullTextIndex(this IndexBuilder indexBuilder, bool? value = true)
+ public static IndexBuilder IsFullTextIndex(this IndexBuilder indexBuilder, bool fullTextIndex = true)
{
- indexBuilder.Metadata.SetIsFullTextIndex(value);
+ indexBuilder.Metadata.SetIsFullTextIndex(fullTextIndex);
return indexBuilder;
}
@@ -122,12 +128,12 @@ public static IndexBuilder IsFullTextIndex(this IndexBuilder indexBuilder, bool?
/// Accessing Azure Cosmos DB with EF Core for more information and examples.
///
/// The builder for the index being configured.
- /// The value indicating whether the index is configured for Full-text search.
+ /// The value indicating whether the index is configured for Full-text search.
/// A builder to further configure the index.
public static IndexBuilder IsFullTextIndex(
this IndexBuilder indexBuilder,
- bool? value = true)
- => (IndexBuilder)IsFullTextIndex((IndexBuilder)indexBuilder, value);
+ bool fullTextIndex = true)
+ => (IndexBuilder)IsFullTextIndex((IndexBuilder)indexBuilder, fullTextIndex);
///
/// Configures the index as a full-text index.
@@ -138,7 +144,7 @@ public static IndexBuilder IsFullTextIndex(
/// Accessing Azure Cosmos DB with EF Core for more information and examples.
///
/// The builder for the index being configured.
- /// The value indicating whether the index is configured for Full-text search.
+ /// The value indicating whether the index is configured for Full-text search.
/// Indicates whether the configuration was specified using a data annotation.
///
/// The same builder instance if the configuration was applied,
@@ -146,12 +152,12 @@ public static IndexBuilder IsFullTextIndex(
///
public static IConventionIndexBuilder? IsFullTextIndex(
this IConventionIndexBuilder indexBuilder,
- bool? value,
+ bool? fullTextIndex,
bool fromDataAnnotation = false)
{
if (indexBuilder.CanSetIsFullTextIndex(fromDataAnnotation))
{
- indexBuilder.Metadata.SetIsFullTextIndex(value, fromDataAnnotation);
+ indexBuilder.Metadata.SetIsFullTextIndex(fullTextIndex, fromDataAnnotation);
return indexBuilder;
}
@@ -166,12 +172,12 @@ public static IndexBuilder IsFullTextIndex(
/// Accessing Azure Cosmos DB with EF Core for more information and examples.
///
/// The builder for the index being configured.
- /// The value indicating whether the index is configured for Full-text search.
+ /// The value indicating whether the index is configured for Full-text search.
/// Indicates whether the configuration was specified using a data annotation.
/// if the index can be configured as a Full-text index.
public static bool CanSetIsFullTextIndex(
this IConventionIndexBuilder indexBuilder,
- bool? value,
+ bool? fullTextIndex,
bool fromDataAnnotation = false)
- => indexBuilder.CanSetAnnotation(CosmosAnnotationNames.FullTextIndex, value, fromDataAnnotation);
+ => indexBuilder.CanSetAnnotation(CosmosAnnotationNames.FullTextIndex, fullTextIndex, fromDataAnnotation);
}
diff --git a/src/EFCore.Cosmos/Extensions/CosmosIndexExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosIndexExtensions.cs
index 5aabcc0f8b6..078262d9f69 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosIndexExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosIndexExtensions.cs
@@ -32,20 +32,32 @@ public static class CosmosIndexExtensions
///
/// The index.
/// The index type to use.
- public static void SetVectorIndexType(this IMutableIndex index, VectorIndexType? indexType)
- => index.SetAnnotation(CosmosAnnotationNames.VectorIndexType, indexType);
+ /// The value indicating whether the index is configured as a vector index.
+ public static void SetVectorIndexType(this IMutableIndex index, VectorIndexType? indexType, bool? vectorIndex)
+ {
+ if (vectorIndex == true)
+ {
+ index.SetAnnotation(CosmosAnnotationNames.VectorIndexType, indexType);
+ }
+ else
+ {
+ index.RemoveAnnotation(CosmosAnnotationNames.VectorIndexType);
+ }
+ }
///
/// Sets the vector index type to use, such as "flat", "diskANN", or "quantizedFlat".
/// See Vector Search in Azure Cosmos DB for NoSQL for more information.
///
- /// The index type to use.
/// The index.
+ /// The index type to use.
+ /// The value indicating whether the index is configured as a vector index.
/// Indicates whether the configuration was specified using a data annotation.
/// The configured value.
public static string? SetVectorIndexType(
this IConventionIndex index,
VectorIndexType? indexType,
+ bool? vectorIndex,
bool fromDataAnnotation = false)
=> (string?)index.SetAnnotation(
CosmosAnnotationNames.VectorIndexType,
@@ -76,25 +88,25 @@ public static void SetVectorIndexType(this IMutableIndex index, VectorIndexType?
/// See Full-text search in Azure Cosmos DB for NoSQL for more information.
///
/// The index.
- /// The value indicating whether the index is configured for full-text search.
- public static void SetIsFullTextIndex(this IMutableIndex index, bool? value)
- => index.SetAnnotation(CosmosAnnotationNames.FullTextIndex, value);
+ /// The value indicating whether the index is configured for full-text search.
+ public static void SetIsFullTextIndex(this IMutableIndex index, bool? fullTextIndex)
+ => index.SetAnnotation(CosmosAnnotationNames.FullTextIndex, fullTextIndex);
///
/// Configures the index for full-text search.
/// See Full-text search in Azure Cosmos DB for NoSQL for more information.
///
/// The index.
- /// The value indicating whether the index is configured for full-text search.
+ /// The value indicating whether the index is configured for full-text search.
/// Indicates whether the configuration was specified using a data annotation.
/// The configured value.
public static string? SetIsFullTextIndex(
this IConventionIndex index,
- bool? value,
+ bool? fullTextIndex,
bool fromDataAnnotation = false)
=> (string?)index.SetAnnotation(
CosmosAnnotationNames.FullTextIndex,
- value,
+ fullTextIndex,
fromDataAnnotation)?.Value;
///
diff --git a/src/EFCore.Cosmos/Extensions/CosmosModelBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosModelBuilderExtensions.cs
index fa04b61a020..d5b25616597 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosModelBuilderExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosModelBuilderExtensions.cs
@@ -257,4 +257,70 @@ public static bool CanSetThroughput(
? existingThroughput?.Throughput == throughput
: existingThroughput?.AutoscaleMaxThroughput == throughput;
}
+
+ ///
+ /// Configures a default language to use for full-text search at database scope.
+ ///
+ ///
+ /// See Modeling entity types and relationships , and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The model builder.
+ /// The default language.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ModelBuilder HasDefaultFullTextLanguage(
+ this ModelBuilder modelBuilder,
+ string? language)
+ {
+ modelBuilder.Model.SetDefaultFullTextSearchLanguage(language);
+
+ return modelBuilder;
+ }
+
+ ///
+ /// Configures a default language to use for full-text search at database scope.
+ ///
+ ///
+ /// See Modeling entity types and relationships , and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The model builder.
+ /// The default language.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the configuration was applied,
+ /// otherwise.
+ ///
+ public static IConventionModelBuilder? HasDefaultFullTextLanguage(
+ this IConventionModelBuilder modelBuilder,
+ string? language,
+ bool fromDataAnnotation = false)
+ {
+ if (!modelBuilder.CanSetDefaultFullTextLanguage(language, fromDataAnnotation))
+ {
+ return null;
+ }
+
+ modelBuilder.Metadata.SetDefaultFullTextSearchLanguage(language, fromDataAnnotation);
+
+ return modelBuilder;
+ }
+
+ ///
+ /// Returns a value indicating whether the default full-text language can be set
+ /// from the current configuration source
+ ///
+ ///
+ /// See Modeling entity types and relationships , and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The model builder.
+ /// The default language.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the configuration can be applied.
+ public static bool CanSetDefaultFullTextLanguage(
+ this IConventionModelBuilder modelBuilder,
+ string? language,
+ bool fromDataAnnotation = false)
+ => modelBuilder.CanSetAnnotation(CosmosAnnotationNames.DefaultFullTextSearchLanguage, language, fromDataAnnotation);
}
diff --git a/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs
index 93c56dbdaba..be6f8f79366 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs
@@ -197,4 +197,46 @@ public static void SetThroughput(this IMutableModel model, int? throughput, bool
public static ConfigurationSource? GetThroughputConfigurationSource(this IConventionModel model)
=> model.FindAnnotation(CosmosAnnotationNames.Throughput)
?.GetConfigurationSource();
+
+ ///
+ /// Returns the default language for the full-text search at database scope.
+ ///
+ /// The model.
+ /// The default language for the full-text search.
+ public static string? GetDefaultFullTextSearchLanguage(this IReadOnlyModel model)
+ => (string?)model[CosmosAnnotationNames.DefaultFullTextSearchLanguage];
+
+ ///
+ /// Sets the default language for the full-text search at database scope.
+ ///
+ /// The model.
+ /// The default language for the full-text search.
+ public static void SetDefaultFullTextSearchLanguage(this IMutableModel model, string? language)
+ => model.SetOrRemoveAnnotation(
+ CosmosAnnotationNames.DefaultFullTextSearchLanguage,
+ language);
+
+ ///
+ /// Sets the default language for the full-text search at database scope.
+ ///
+ /// The model.
+ /// The default language for the full-text search.
+ /// Indicates whether the configuration was specified using a data annotation.
+ public static string? SetDefaultFullTextSearchLanguage(
+ this IConventionModel model,
+ string? language,
+ bool fromDataAnnotation = false)
+ => (string?)model.SetOrRemoveAnnotation(
+ CosmosAnnotationNames.DefaultFullTextSearchLanguage,
+ language,
+ fromDataAnnotation)?.Value;
+
+ ///
+ /// Gets the for the default full-text search language at database scope.
+ ///
+ /// The model type to find configuration source for.
+ /// The for the default full-text search language.
+ public static ConfigurationSource? GetDefaultFullTextSearchLanguageConfigurationSource(this IConventionModel model)
+ => model.FindAnnotation(CosmosAnnotationNames.DefaultFullTextSearchLanguage)
+ ?.GetConfigurationSource();
}
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs
index 1f6e0f477fb..8fd7565bc71 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs
@@ -169,11 +169,10 @@ protected virtual void ValidateSharedContainerCompatibility(
IDiagnosticsLogger logger)
{
var discriminatorValues = new Dictionary();
- List partitionKeyStoreNames = new();
+ List partitionKeyStoreNames = [];
int? analyticalTtl = null;
int? defaultTtl = null;
ThroughputProperties? throughput = null;
- string? defaultFullTextSearchLanguage = null;
IEntityType? firstEntityType = null;
bool? isDiscriminatorMappingComplete = null;
@@ -338,27 +337,6 @@ protected virtual void ValidateSharedContainerCompatibility(
CosmosStrings.ThroughputTypeMismatch(manualType.DisplayName(), autoscaleType.DisplayName(), container));
}
}
-
- var currentFullTextSearchDefaultLanguage = entityType.GetDefaultFullTextSearchLanguage();
- if (currentFullTextSearchDefaultLanguage != null)
- {
- if (defaultFullTextSearchLanguage == null)
- {
- defaultFullTextSearchLanguage = currentFullTextSearchDefaultLanguage;
- }
- else if (defaultFullTextSearchLanguage != currentFullTextSearchDefaultLanguage)
- {
- var conflictingEntityType = mappedTypes.First(et => et.GetDefaultFullTextSearchLanguage() != null);
-
- throw new InvalidOperationException(
- CosmosStrings.FullTextSearchDefaultLanguageMismatch(
- defaultFullTextSearchLanguage,
- conflictingEntityType.DisplayName(),
- entityType.DisplayName(),
- currentFullTextSearchDefaultLanguage,
- container));
- }
- }
}
}
diff --git a/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
index 425c3db1266..ff9b790d385 100644
--- a/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
+++ b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
@@ -187,14 +187,6 @@ public static string FullTextSearchConfiguredForUnsupportedPropertyType(object?
GetString("FullTextSearchConfiguredForUnsupportedPropertyType", nameof(entityType), nameof(property), nameof(clrType)),
entityType, property, clrType);
- ///
- /// The default full-text search language was configured to '{defaultLanguage1}' on '{entityType1}', but on '{entityType2}' it was configured to '{defaultLanguage2}'. All entity types mapped to the same container '{container}' must be configured with the same default full-text search language.
- ///
- public static string FullTextSearchDefaultLanguageMismatch(object? defaultLanguage1, object? entityType1, object? entityType2, object? defaultLanguage2, object? container)
- => string.Format(
- GetString("FullTextSearchDefaultLanguageMismatch", nameof(defaultLanguage1), nameof(entityType1), nameof(entityType2), nameof(defaultLanguage2), nameof(container)),
- defaultLanguage1, entityType1, entityType2, defaultLanguage2, container);
-
///
/// 'HasShadowId' was called on a non-root entity type '{entityType}'. JSON 'id' configuration can only be made on the document root.
///
diff --git a/src/EFCore.Cosmos/Properties/CosmosStrings.resx b/src/EFCore.Cosmos/Properties/CosmosStrings.resx
index 6fa5a8ecbed..b42289a5704 100644
--- a/src/EFCore.Cosmos/Properties/CosmosStrings.resx
+++ b/src/EFCore.Cosmos/Properties/CosmosStrings.resx
@@ -183,9 +183,6 @@
Property '{entityType}.{property}' was configured for full-text search, but has type '{clrType}'; only string properties can be configured for full-text search.
-
- The default full-text search language was configured to '{defaultLanguage1}' on '{entityType1}', but on '{entityType2}' it was configured to '{defaultLanguage2}'. All entity types mapped to the same container '{container}' must be configured with the same default full-text search language.
-
'HasShadowId' was called on a non-root entity type '{entityType}'. JSON 'id' configuration can only be made on the document root.
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs
index 4dc784ec67a..8d475d7268d 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs
@@ -117,6 +117,7 @@ private static IEnumerable GetContainersToCreate(IModel mod
mappedTypes.Add(entityType);
}
+ var defaultFullTextLanguage = model.GetDefaultFullTextSearchLanguage();
foreach (var (containerName, mappedTypes) in containers)
{
IReadOnlyList partitionKeyStoreNames = Array.Empty();
@@ -125,7 +126,6 @@ private static IEnumerable GetContainersToCreate(IModel mod
ThroughputProperties? throughput = null;
var indexes = new List();
var vectors = new List<(IProperty Property, CosmosVectorType VectorType)>();
- string? defaultFullTextLanguage = null;
var fullTextProperties = new List<(IProperty Property, string? Language)>();
foreach (var entityType in mappedTypes)
@@ -138,7 +138,6 @@ private static IEnumerable GetContainersToCreate(IModel mod
analyticalTtl ??= entityType.GetAnalyticalStoreTimeToLive();
defaultTtl ??= entityType.GetDefaultTimeToLive();
throughput ??= entityType.GetThroughput();
- defaultFullTextLanguage ??= entityType.GetDefaultFullTextSearchLanguage();
ProcessEntityType(entityType, indexes, vectors, fullTextProperties);
}
diff --git a/src/EFCore.Relational/Extensions/RelationalComplexCollectionBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalComplexCollectionBuilderExtensions.cs
index bcd09203a6d..97196af09aa 100644
--- a/src/EFCore.Relational/Extensions/RelationalComplexCollectionBuilderExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalComplexCollectionBuilderExtensions.cs
@@ -38,7 +38,7 @@ public static ComplexCollectionBuilder ToJson(
public static ComplexCollectionBuilder ToJson(
this ComplexCollectionBuilder complexCollectionBuilder,
string? jsonColumnName = null)
- where TComplex : class
+ where TComplex : notnull
=> (ComplexCollectionBuilder)ToJson((ComplexCollectionBuilder)complexCollectionBuilder, jsonColumnName);
///
diff --git a/src/EFCore.Relational/Extensions/RelationalComplexCollectionTypePropertyBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalComplexCollectionTypePropertyBuilderExtensions.cs
new file mode 100644
index 00000000000..136edb2463e
--- /dev/null
+++ b/src/EFCore.Relational/Extensions/RelationalComplexCollectionTypePropertyBuilderExtensions.cs
@@ -0,0 +1,45 @@
+// 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;
+
+///
+/// Relational database specific extension methods for .
+///
+///
+/// See Modeling entity types and relationships for more information and examples.
+///
+public static class RelationalComplexCollectionTypePropertyBuilderExtensions
+{
+ ///
+ /// Configures the property of an entity mapped to a JSON column, mapping the entity property to a specific JSON property,
+ /// rather than using the entity property name.
+ ///
+ /// The builder for the property being configured.
+ /// JSON property name to be used.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexCollectionTypePropertyBuilder HasJsonPropertyName(
+ this ComplexCollectionTypePropertyBuilder propertyBuilder,
+ string? name)
+ {
+ Check.NullButNotEmpty(name);
+
+ propertyBuilder.Metadata.SetJsonPropertyName(name);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the property of an entity mapped to a JSON column, mapping the entity property to a specific JSON property,
+ /// rather than using the entity property name.
+ ///
+ /// The builder for the property being configured.
+ /// JSON property name to be used.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexCollectionTypePropertyBuilder HasJsonPropertyName(
+ this ComplexCollectionTypePropertyBuilder propertyBuilder,
+ string? name)
+ => (ComplexCollectionTypePropertyBuilder)HasJsonPropertyName((ComplexCollectionTypePropertyBuilder)propertyBuilder, name);
+}
diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
index ea8979aeb5e..0177582274d 100644
--- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
@@ -2097,7 +2097,7 @@ public static void SetJsonPropertyName(this IMutableProperty property, string? n
: (string?)property[RelationalAnnotationNames.DefaultConstraintName]
?? (ShouldHaveDefaultConstraintName(property)
&& StoreObjectIdentifier.Create(property.DeclaringType, StoreObjectType.Table) is StoreObjectIdentifier table
- ? property.GenerateDefaultConstraintName(table)
+ ? property.GetDefaultDefaultConstraintName(table)
: null);
///
@@ -2110,7 +2110,7 @@ public static void SetJsonPropertyName(this IMutableProperty property, string? n
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
: (string?)property[RelationalAnnotationNames.DefaultConstraintName]
?? (ShouldHaveDefaultConstraintName(property)
- ? property.GenerateDefaultConstraintName(storeObject)
+ ? property.GetDefaultDefaultConstraintName(storeObject)
: null);
private static bool ShouldHaveDefaultConstraintName(IReadOnlyProperty property)
@@ -2123,7 +2123,7 @@ private static bool ShouldHaveDefaultConstraintName(IReadOnlyProperty property)
///
/// The property.
/// The store object identifier to generate the name for.
- public static string GenerateDefaultConstraintName(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
+ public static string GetDefaultDefaultConstraintName(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
{
var candidate = $"DF_{storeObject.Name}_{property.GetColumnName(storeObject)}";
diff --git a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs
index 24fbda9033a..05e17e4a1b0 100644
--- a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs
+++ b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs
@@ -277,7 +277,7 @@ public override IEnumerable For(IColumn column, bool designTime)
// (we already checked that in the finalize model convention step)
yield return new Annotation(
RelationalAnnotationNames.DefaultConstraintName,
- mappedProperty.GenerateDefaultConstraintName(table));
+ mappedProperty.GetDefaultDefaultConstraintName(table));
}
}
}
diff --git a/src/EFCore/ChangeTracking/Internal/InternalComplexEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalComplexEntry.cs
index b460e303363..5b8be17c366 100644
--- a/src/EFCore/ChangeTracking/Internal/InternalComplexEntry.cs
+++ b/src/EFCore/ChangeTracking/Internal/InternalComplexEntry.cs
@@ -273,10 +273,7 @@ public override void AcceptChanges()
///
public string GetPropertyPath(IReadOnlyProperty property)
{
- Check.DebugAssert(property.DeclaringType == StructuralType
- || property.DeclaringType.ContainingType == StructuralType
- || StructuralType.ClrType == typeof(object), // For testing
- "Property " + property.Name + " not contained under " + StructuralType.Name);
+ StructuralType.CheckContains(property);
return GetPropertyPath() + "." + GetShortNameChain(property.DeclaringType) + property.Name;
}
diff --git a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs
index 2e42fcd12a6..f4bb13c6d81 100644
--- a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs
+++ b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs
@@ -164,7 +164,7 @@ protected virtual Expression CreateSnapshotExpression(
{
Expression.Assign(
structuralTypeVariable,
- propertyBases[0]!.DeclaringType switch
+ (IRuntimeTypeBase)propertyBases[0]!.DeclaringType switch
{
IComplexType declaringComplexType when declaringComplexType.ComplexProperty.IsCollection
=> PropertyAccessorsFactory.CreateComplexCollectionElementAccess(
@@ -175,7 +175,7 @@ IComplexType declaringComplexType when declaringComplexType.ComplexProperty.IsCo
indicesExpression,
fromDeclaringType: false,
fromEntity: true),
- { ContainingType: IComplexType collectionComplexType }
+ { ContainingEntryType: IComplexType collectionComplexType }
=> PropertyAccessorsFactory.CreateComplexCollectionElementAccess(
collectionComplexType.ComplexProperty,
Expression.Convert(
diff --git a/src/EFCore/Metadata/Builders/ComplexCollectionBuilder.cs b/src/EFCore/Metadata/Builders/ComplexCollectionBuilder.cs
index 71be133c83e..e27b4011792 100644
--- a/src/EFCore/Metadata/Builders/ComplexCollectionBuilder.cs
+++ b/src/EFCore/Metadata/Builders/ComplexCollectionBuilder.cs
@@ -119,7 +119,7 @@ public virtual ComplexCollectionBuilder IsRequired(bool required = true)
///
/// The name of the property to be configured.
/// An object that can be used to configure the property.
- public virtual ComplexTypePropertyBuilder Property(string propertyName)
+ public virtual ComplexCollectionTypePropertyBuilder Property(string propertyName)
=> new(
TypeBuilder.Property(
Check.NotEmpty(propertyName),
@@ -139,7 +139,7 @@ public virtual ComplexTypePropertyBuilder Property(string propertyName)
/// The type of the property to be configured.
/// The name of the property to be configured.
/// An object that can be used to configure the property.
- public virtual ComplexTypePropertyBuilder Property(string propertyName)
+ public virtual ComplexCollectionTypePropertyBuilder Property(string propertyName)
=> new(
TypeBuilder.Property(
typeof(TProperty),
@@ -159,7 +159,7 @@ public virtual ComplexTypePropertyBuilder Property(string
/// The type of the property to be configured.
/// The name of the property to be configured.
/// An object that can be used to configure the property.
- public virtual ComplexTypePropertyBuilder Property(Type propertyType, string propertyName)
+ public virtual ComplexCollectionTypePropertyBuilder Property(Type propertyType, string propertyName)
=> new(
TypeBuilder.Property(
Check.NotNull(propertyType),
@@ -237,7 +237,7 @@ public virtual ComplexTypePrimitiveCollectionBuilder PrimitiveCollection(Type pr
/// The type of the property to be configured.
/// The name of the property to be configured.
/// An object that can be used to configure the property.
- public virtual ComplexTypePropertyBuilder IndexerProperty
+ public virtual ComplexCollectionTypePropertyBuilder IndexerProperty
<[DynamicallyAccessedMembers(IProperty.DynamicallyAccessedMemberTypes)] TProperty>(string propertyName)
=> new(
TypeBuilder.IndexerProperty(
@@ -256,7 +256,7 @@ public virtual ComplexTypePropertyBuilder IndexerProperty
/// The type of the property to be configured.
/// The name of the property to be configured.
/// An object that can be used to configure the property.
- public virtual ComplexTypePropertyBuilder IndexerProperty(
+ public virtual ComplexCollectionTypePropertyBuilder IndexerProperty(
[DynamicallyAccessedMembers(IProperty.DynamicallyAccessedMemberTypes)] Type propertyType,
string propertyName)
{
@@ -586,7 +586,7 @@ public virtual ComplexCollectionBuilder ComplexCollection
/// The name of the property to be configured.
/// An action that performs configuration of the property.
- /// An object that can be used to configure the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
public virtual ComplexCollectionBuilder ComplexCollection(string propertyName, Action buildAction)
{
Check.NotNull(buildAction);
@@ -728,7 +728,7 @@ public virtual ComplexCollectionBuilder ComplexCollection(
/// The type of the property to be configured.
/// The name of the property to be configured.
/// An action that performs configuration of the property.
- /// An object that can be used to configure the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
public virtual ComplexCollectionBuilder ComplexCollection(Type propertyType, string propertyName, Action buildAction)
{
Check.NotNull(buildAction);
diff --git a/src/EFCore/Metadata/Builders/ComplexCollectionBuilder`.cs b/src/EFCore/Metadata/Builders/ComplexCollectionBuilder`.cs
index b5c81a7fcc3..a2929ae24f9 100644
--- a/src/EFCore/Metadata/Builders/ComplexCollectionBuilder`.cs
+++ b/src/EFCore/Metadata/Builders/ComplexCollectionBuilder`.cs
@@ -72,7 +72,7 @@ public ComplexCollectionBuilder(IMutableComplexProperty complexProperty)
/// blog => blog.Url ).
///
/// An object that can be used to configure the property.
- public virtual ComplexTypePropertyBuilder Property(Expression> propertyExpression)
+ public virtual ComplexCollectionTypePropertyBuilder Property(Expression> propertyExpression)
=> new(
TypeBuilder.Property(
Check.NotNull(propertyExpression).GetMemberAccess(), ConfigurationSource.Explicit)!
diff --git a/src/EFCore/Metadata/Builders/ComplexCollectionTypePropertyBuilder.cs b/src/EFCore/Metadata/Builders/ComplexCollectionTypePropertyBuilder.cs
new file mode 100644
index 00000000000..6a27c5dbb2d
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexCollectionTypePropertyBuilder.cs
@@ -0,0 +1,548 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+/// Provides a simple API for configuring a on a complex collection type.
+///
+///
+///
+/// Instances of this class are returned from methods when using the API
+/// and it is not designed to be directly constructed in your application code.
+///
+///
+/// See Modeling complex types and relationships for more information and
+/// examples.
+///
+///
+public class ComplexCollectionTypePropertyBuilder : IInfrastructure
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public ComplexCollectionTypePropertyBuilder(IMutableProperty property)
+ {
+ Check.NotNull(property);
+
+ Builder = ((Property)property).Builder;
+ }
+
+ ///
+ /// The internal builder being used to configure the property.
+ ///
+ IConventionPropertyBuilder IInfrastructure.Instance
+ => Builder;
+
+ private InternalPropertyBuilder Builder { get; }
+
+ ///
+ /// The property being configured.
+ ///
+ public virtual IMutableProperty Metadata
+ => Builder.Metadata;
+
+ ///
+ /// Adds or updates an annotation on the property. If an annotation with the key specified in
+ /// already exists its value will be updated.
+ ///
+ /// The key of the annotation to be added or updated.
+ /// The value to be stored in the annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasAnnotation(string annotation, object? value)
+ {
+ Check.NotEmpty(annotation);
+
+ Builder.HasAnnotation(annotation, value, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures whether this property must have a value assigned or is a valid value.
+ /// A property can only be configured as non-required if it is based on a CLR type that can be
+ /// assigned .
+ ///
+ /// A value indicating whether the property is required.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder IsRequired(bool required = true)
+ {
+ Builder.IsRequired(required, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the value that will be used to determine if the property has been set or not. If the property is set to the
+ /// sentinel value, then it is considered not set. By default, the sentinel value is the CLR default value for the type of
+ /// the property.
+ ///
+ /// The sentinel value.
+ /// The same builder instance if the configuration was applied, otherwise.
+ public virtual ComplexCollectionTypePropertyBuilder HasSentinel(object? sentinel)
+ {
+ Builder.HasSentinel(sentinel, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures whether the property as capable of persisting unicode characters.
+ /// Can only be set on properties.
+ ///
+ /// A value indicating whether the property can contain unicode characters.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder IsUnicode(bool unicode = true)
+ {
+ Builder.IsUnicode(unicode, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the that will generate values for this property.
+ ///
+ ///
+ ///
+ /// Values are generated when the entity is added to the context using, for example,
+ /// . Values are generated only when the property is assigned
+ /// the CLR default value ( for string , 0 for int ,
+ /// Guid.Empty for Guid , etc.).
+ ///
+ ///
+ /// A single instance of this type will be created and used to generate values for this property in all
+ /// instances of the complex type. The type must be instantiable and have a parameterless constructor.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasValueGenerator
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TGenerator>()
+ where TGenerator : ValueGenerator
+ {
+ Builder.HasValueGenerator(typeof(TGenerator), ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the that will generate values for this property.
+ ///
+ ///
+ ///
+ /// Values are generated when the entity is added to the context using, for example,
+ /// . Values are generated only when the property is assigned
+ /// the CLR default value ( for string , 0 for int ,
+ /// Guid.Empty for Guid , etc.).
+ ///
+ ///
+ /// A single instance of this type will be created and used to generate values for this property in all
+ /// instances of the complex type. The type must be instantiable and have a parameterless constructor.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// Setting does not disable value generation for this property, it just clears any generator explicitly
+ /// configured for this property. The database provider may still have a value generator for the property type.
+ ///
+ ///
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasValueGenerator(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? valueGeneratorType)
+ {
+ Builder.HasValueGenerator(valueGeneratorType, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the for creating a
+ /// to use to generate values for this property.
+ ///
+ ///
+ ///
+ /// Values are generated when the entity is added to the context using, for example,
+ /// . Values are generated only when the property is assigned
+ /// the CLR default value ( for string , 0 for int ,
+ /// Guid.Empty for Guid , etc.).
+ ///
+ ///
+ /// A single instance of this type will be created and used to generate values for this property in all
+ /// instances of the complex type. The type must be instantiable and have a parameterless constructor.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// Setting does not disable value generation for this property, it just clears any generator explicitly
+ /// configured for this property. The database provider may still have a value generator for the property type.
+ ///
+ ///
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasValueGeneratorFactory
+ <[DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] TFactory>()
+ where TFactory : ValueGeneratorFactory
+ => HasValueGeneratorFactory(typeof(TFactory));
+
+ ///
+ /// Configures the for creating a
+ /// to use to generate values for this property.
+ ///
+ ///
+ ///
+ /// Values are generated when the entity is added to the context using, for example,
+ /// . Values are generated only when the property is assigned
+ /// the CLR default value ( for string , 0 for int ,
+ /// Guid.Empty for Guid , etc.).
+ ///
+ ///
+ /// A single instance of this type will be created and used to generate values for this property in all
+ /// instances of the complex type. The type must be instantiable and have a parameterless constructor.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// Setting does not disable value generation for this property, it just clears any generator explicitly
+ /// configured for this property. The database provider may still have a value generator for the property type.
+ ///
+ ///
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasValueGeneratorFactory(
+ [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)]
+ Type? valueGeneratorFactoryType)
+ {
+ Builder.HasValueGeneratorFactory(valueGeneratorFactoryType, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Sets the backing field to use for this property.
+ ///
+ ///
+ ///
+ /// Backing fields are normally found by convention.
+ /// This method is useful for setting backing fields explicitly in cases where the
+ /// correct field is not found by convention.
+ ///
+ ///
+ /// By default, the backing field, if one is found or has been specified, is used when
+ /// new objects are constructed, typically when entities are queried from the database.
+ /// Properties are used for all other accesses. This can be changed by calling
+ /// .
+ ///
+ ///
+ /// See Backing fields for more information and examples.
+ ///
+ ///
+ /// The field name.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasField(string fieldName)
+ {
+ Check.NotEmpty(fieldName);
+
+ Builder.HasField(fieldName, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Sets the to use for this property.
+ ///
+ ///
+ ///
+ /// By default, the backing field, if one is found by convention or has been specified, is used when
+ /// new objects are constructed, typically when entities are queried from the database.
+ /// Properties are used for all other accesses. Calling this method will change that behavior
+ /// for this property as described in the enum.
+ ///
+ ///
+ /// Calling this method overrides for this property any access mode that was set on the
+ /// complex type or model.
+ ///
+ ///
+ /// The to use for this property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder UsePropertyAccessMode(PropertyAccessMode propertyAccessMode)
+ {
+ Builder.UsePropertyAccessMode(propertyAccessMode, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TConversion>()
+ => HasConversion(typeof(TConversion));
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? conversionType)
+ {
+ if (typeof(ValueConverter).IsAssignableFrom(conversionType))
+ {
+ Builder.HasConverter(conversionType, ConfigurationSource.Explicit);
+ }
+ else
+ {
+ Builder.HasConversion(conversionType, ConfigurationSource.Explicit);
+ }
+
+ return this;
+ }
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given .
+ ///
+ /// The converter to use.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(ValueConverter? converter)
+ => HasConversion(converter, null, null);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The comparer to use for values before conversion.
+ /// The type to convert to and from or a type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TConversion>(
+ ValueComparer? valueComparer)
+ => HasConversion(typeof(TConversion), valueComparer);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The comparer to use for values before conversion.
+ /// The comparer to use for the provider values.
+ /// The type to convert to and from or a type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TConversion>(
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => HasConversion(typeof(TConversion), valueComparer, providerComparer);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The comparer to use for values before conversion.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type conversionType,
+ ValueComparer? valueComparer)
+ => HasConversion(conversionType, valueComparer, null);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The comparer to use for values before conversion.
+ /// The comparer to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type conversionType,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ {
+ Check.NotNull(conversionType);
+
+ if (typeof(ValueConverter).IsAssignableFrom(conversionType))
+ {
+ Builder.HasConverter(conversionType, ConfigurationSource.Explicit);
+ }
+ else
+ {
+ Builder.HasConversion(conversionType, ConfigurationSource.Explicit);
+ }
+
+ Builder.HasValueComparer(valueComparer, ConfigurationSource.Explicit);
+ Builder.HasProviderValueComparer(providerComparer, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given .
+ ///
+ /// The converter to use.
+ /// The comparer to use for values before conversion.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(ValueConverter? converter, ValueComparer? valueComparer)
+ => HasConversion(converter, valueComparer, null);
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given .
+ ///
+ /// The converter to use.
+ /// The comparer to use for values before conversion.
+ /// The comparer to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ ValueConverter? converter,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ {
+ Builder.HasConversion(converter, ConfigurationSource.Explicit);
+ Builder.HasValueComparer(valueComparer, ConfigurationSource.Explicit);
+ Builder.HasProviderValueComparer(providerComparer, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TConversion,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TComparer>()
+ where TComparer : ValueComparer
+ => HasConversion(typeof(TConversion), typeof(TComparer));
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// A type that inherits from .
+ /// A type that inherits from to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TConversion,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TComparer,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TProviderComparer>()
+ where TComparer : ValueComparer
+ where TProviderComparer : ValueComparer
+ => HasConversion(typeof(TConversion), typeof(TComparer), typeof(TProviderComparer));
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type conversionType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType)
+ => HasConversion(conversionType, comparerType, null);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// A type that inherits from .
+ /// A type that inherits from to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type conversionType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? providerComparerType)
+ {
+ Check.NotNull(conversionType);
+
+ if (typeof(ValueConverter).IsAssignableFrom(conversionType))
+ {
+ Builder.HasConverter(conversionType, ConfigurationSource.Explicit);
+ }
+ else
+ {
+ Builder.HasConversion(conversionType, ConfigurationSource.Explicit);
+ }
+
+ Builder.HasValueComparer(comparerType, ConfigurationSource.Explicit);
+ Builder.HasProviderValueComparer(providerComparerType, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ #region Hidden System.Object members
+
+ ///
+ /// Returns a string that represents the current object.
+ ///
+ /// A string that represents the current object.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override string? ToString()
+ => base.ToString();
+
+ ///
+ /// Determines whether the specified object is equal to the current object.
+ ///
+ /// The object to compare with the current object.
+ /// if the specified object is equal to the current object; otherwise, .
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ // ReSharper disable once BaseObjectEqualsIsObjectEquals
+ public override bool Equals(object? obj)
+ => base.Equals(obj);
+
+ ///
+ /// Serves as the default hash function.
+ ///
+ /// A hash code for the current object.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ // ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
+ public override int GetHashCode()
+ => base.GetHashCode();
+
+ #endregion
+}
diff --git a/src/EFCore/Metadata/Builders/ComplexCollectionTypePropertyBuilder`.cs b/src/EFCore/Metadata/Builders/ComplexCollectionTypePropertyBuilder`.cs
new file mode 100644
index 00000000000..de6fd0abe56
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexCollectionTypePropertyBuilder`.cs
@@ -0,0 +1,515 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+/// Provides a simple API for configuring a on a complex collection type.
+///
+///
+///
+/// Instances of this class are returned from methods when using the API
+/// and it is not designed to be directly constructed in your application code.
+///
+///
+/// See Modeling complex types and relationships for more information and
+/// examples.
+///
+///
+public class ComplexCollectionTypePropertyBuilder : ComplexCollectionTypePropertyBuilder
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public ComplexCollectionTypePropertyBuilder(IMutableProperty property)
+ : base(property)
+ {
+ }
+
+ ///
+ /// Adds or updates an annotation on the property. If an annotation with the key specified in
+ /// already exists its value will be updated.
+ ///
+ /// The key of the annotation to be added or updated.
+ /// The value to be stored in the annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasAnnotation(string annotation, object? value)
+ => (ComplexCollectionTypePropertyBuilder)base.HasAnnotation(annotation, value);
+
+ ///
+ /// Configures whether this property must have a value assigned or whether null is a valid value.
+ /// A property can only be configured as non-required if it is based on a CLR type that can be
+ /// assigned .
+ ///
+ /// A value indicating whether the property is required.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder IsRequired(bool required = true)
+ => (ComplexCollectionTypePropertyBuilder)base.IsRequired(required);
+
+ ///
+ /// Configures the value that will be used to determine if the property has been set or not. If the property is set to the
+ /// sentinel value, then it is considered not set. By default, the sentinel value is the CLR default value for the type of
+ /// the property.
+ ///
+ /// The sentinel value.
+ /// The same builder instance if the configuration was applied, otherwise.
+ public new virtual ComplexCollectionTypePropertyBuilder HasSentinel(object? sentinel)
+ => (ComplexCollectionTypePropertyBuilder)base.HasSentinel(sentinel);
+
+ ///
+ /// Configures the value that will be used to determine if the property has been set or not. If the property is set to the
+ /// sentinel value, then it is considered not set. By default, the sentinel value is the CLR default value for the type of
+ /// the property.
+ ///
+ /// The sentinel value.
+ /// The same builder instance if the configuration was applied, otherwise.
+ public virtual ComplexCollectionTypePropertyBuilder HasSentinel(TProperty? sentinel)
+ => (ComplexCollectionTypePropertyBuilder)base.HasSentinel(sentinel);
+
+ ///
+ /// Configures the property as capable of persisting unicode characters.
+ /// Can only be set on properties.
+ ///
+ /// A value indicating whether the property can contain unicode characters.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder IsUnicode(bool unicode = true)
+ => (ComplexCollectionTypePropertyBuilder)base.IsUnicode(unicode);
+
+ ///
+ /// Configures the that will generate values for this property.
+ ///
+ ///
+ ///
+ /// Values are generated when the entity is added to the context using, for example,
+ /// . Values are generated only when the property is assigned
+ /// the CLR default value ( for string , 0 for int ,
+ /// Guid.Empty for Guid , etc.).
+ ///
+ ///
+ /// A single instance of this type will be created and used to generate values for this property in all
+ /// instances of the complex type. The type must be instantiable and have a parameterless constructor.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasValueGenerator
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TGenerator>()
+ where TGenerator : ValueGenerator
+ => (ComplexCollectionTypePropertyBuilder)base.HasValueGenerator();
+
+ ///
+ /// Configures the that will generate values for this property.
+ ///
+ ///
+ ///
+ /// Values are generated when the entity is added to the context using, for example,
+ /// . Values are generated only when the property is assigned
+ /// the CLR default value ( for string , 0 for int ,
+ /// Guid.Empty for Guid , etc.).
+ ///
+ ///
+ /// A single instance of this type will be created and used to generate values for this property in all
+ /// instances of the complex type. The type must be instantiable and have a parameterless constructor.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// Setting null does not disable value generation for this property, it just clears any generator explicitly
+ /// configured for this property. The database provider may still have a value generator for the property type.
+ ///
+ ///
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasValueGenerator(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? valueGeneratorType)
+ => (ComplexCollectionTypePropertyBuilder)base.HasValueGenerator(valueGeneratorType);
+
+ ///
+ /// Configures the for creating a
+ /// to use to generate values for this property.
+ ///
+ ///
+ ///
+ /// Values are generated when the entity is added to the context using, for example,
+ /// . Values are generated only when the property is assigned
+ /// the CLR default value ( for string , 0 for int ,
+ /// Guid.Empty for Guid , etc.).
+ ///
+ ///
+ /// A single instance of this type will be created and used to generate values for this property in all
+ /// instances of the complex type. The type must be instantiable and have a parameterless constructor.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// Setting does not disable value generation for this property, it just clears any generator explicitly
+ /// configured for this property. The database provider may still have a value generator for the property type.
+ ///
+ ///
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasValueGeneratorFactory
+ <[DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] TFactory>()
+ where TFactory : ValueGeneratorFactory
+ => (ComplexCollectionTypePropertyBuilder)base.HasValueGeneratorFactory();
+
+ ///
+ /// Configures the for creating a
+ /// to use to generate values for this property.
+ ///
+ ///
+ ///
+ /// Values are generated when the entity is added to the context using, for example,
+ /// . Values are generated only when the property is assigned
+ /// the CLR default value ( for string , 0 for int ,
+ /// Guid.Empty for Guid , etc.).
+ ///
+ ///
+ /// A single instance of this type will be created and used to generate values for this property in all
+ /// instances of the complex type. The type must be instantiable and have a parameterless constructor.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// Setting does not disable value generation for this property, it just clears any generator explicitly
+ /// configured for this property. The database provider may still have a value generator for the property type.
+ ///
+ ///
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasValueGeneratorFactory(
+ [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)]
+ Type? valueGeneratorFactoryType)
+ => (ComplexCollectionTypePropertyBuilder)base.HasValueGeneratorFactory(valueGeneratorFactoryType);
+
+ ///
+ /// Sets the backing field to use for this property.
+ ///
+ ///
+ ///
+ /// Backing fields are normally found by convention.
+ /// This method is useful for setting backing fields explicitly in cases where the
+ /// correct field is not found by convention.
+ ///
+ ///
+ /// By default, the backing field, if one is found or has been specified, is used when
+ /// new objects are constructed, typically when entities are queried from the database.
+ /// Properties are used for all other accesses. This can be changed by calling
+ /// .
+ ///
+ ///
+ /// See Backing fields for more information and examples.
+ ///
+ ///
+ /// The field name.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasField(string fieldName)
+ => (ComplexCollectionTypePropertyBuilder)base.HasField(fieldName);
+
+ ///
+ /// Sets the to use for this property.
+ ///
+ ///
+ ///
+ /// By default, the backing field, if one is found by convention or has been specified, is used when
+ /// new objects are constructed, typically when entities are queried from the database.
+ /// Properties are used for all other accesses. Calling this method will change that behavior
+ /// for this property as described in the enum.
+ ///
+ ///
+ /// Calling this method overrides for this property any access mode that was set on the
+ /// complex type or model.
+ ///
+ ///
+ /// The to use for this property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder UsePropertyAccessMode(PropertyAccessMode propertyAccessMode)
+ => (ComplexCollectionTypePropertyBuilder)base.UsePropertyAccessMode(propertyAccessMode);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TConversion>()
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion();
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? providerClrType)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(providerClrType);
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given conversion expressions.
+ ///
+ /// The store type generated by the conversions.
+ /// An expression to convert objects when writing data to the store.
+ /// An expression to convert objects when reading data from the store.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ Expression> convertToProviderExpression,
+ Expression> convertFromProviderExpression)
+ => HasConversion(
+ new ValueConverter(
+ Check.NotNull(convertToProviderExpression),
+ Check.NotNull(convertFromProviderExpression)));
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given .
+ ///
+ /// The store type generated by the converter.
+ /// The converter to use.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(ValueConverter? converter)
+ => HasConversion((ValueConverter?)converter);
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given .
+ ///
+ /// The converter to use.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion(ValueConverter? converter)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(converter);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The comparer to use for values before conversion.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TConversion>(
+ ValueComparer? valueComparer)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(valueComparer);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The comparer to use for values before conversion.
+ /// The comparer to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TConversion>(
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(valueComparer, providerComparer);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The comparer to use for values before conversion.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type conversionType,
+ ValueComparer? valueComparer)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(conversionType, valueComparer);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// The comparer to use for values before conversion.
+ /// The comparer to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type conversionType,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(conversionType, valueComparer, providerComparer);
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given conversion expressions.
+ ///
+ /// The store type generated by the conversions.
+ /// An expression to convert objects when writing data to the store.
+ /// An expression to convert objects when reading data from the store.
+ /// The comparer to use for values before conversion.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ Expression> convertToProviderExpression,
+ Expression> convertFromProviderExpression,
+ ValueComparer? valueComparer)
+ => HasConversion(
+ new ValueConverter(
+ Check.NotNull(convertToProviderExpression),
+ Check.NotNull(convertFromProviderExpression)),
+ valueComparer);
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given conversion expressions.
+ ///
+ /// The store type generated by the conversions.
+ /// An expression to convert objects when writing data to the store.
+ /// An expression to convert objects when reading data from the store.
+ /// The comparer to use for values before conversion.
+ /// The comparer to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ Expression> convertToProviderExpression,
+ Expression> convertFromProviderExpression,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => HasConversion(
+ new ValueConverter(
+ Check.NotNull(convertToProviderExpression),
+ Check.NotNull(convertFromProviderExpression)),
+ valueComparer,
+ providerComparer);
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given .
+ ///
+ /// The store type generated by the converter.
+ /// The converter to use.
+ /// The comparer to use for values before conversion.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ ValueConverter? converter,
+ ValueComparer? valueComparer)
+ => HasConversion((ValueConverter?)converter, valueComparer);
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given .
+ ///
+ /// The store type generated by the converter.
+ /// The converter to use.
+ /// The comparer to use for values before conversion.
+ /// The comparer to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ ValueConverter? converter,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => HasConversion((ValueConverter?)converter, valueComparer, providerComparer);
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given .
+ ///
+ /// The converter to use.
+ /// The comparer to use for values before conversion.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ ValueConverter? converter,
+ ValueComparer? valueComparer)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(converter, valueComparer);
+
+ ///
+ /// Configures the property so that the property value is converted to and from the database
+ /// using the given .
+ ///
+ /// The converter to use.
+ /// The comparer to use for values before conversion.
+ /// The comparer to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ ValueConverter? converter,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(converter, valueComparer, providerComparer);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TConversion,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TComparer>()
+ where TComparer : ValueComparer
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion();
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// A type that inherits from .
+ /// A type that inherits from to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TConversion,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TComparer,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ TProviderComparer>()
+ where TComparer : ValueComparer
+ where TProviderComparer : ValueComparer
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion();
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// A type that inherits from .
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type conversionType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(conversionType, comparerType);
+
+ ///
+ /// Configures the property so that the property value is converted before
+ /// writing to the database and converted back when reading from the database.
+ ///
+ /// The type to convert to and from or a type that inherits from .
+ /// A type that inherits from .
+ /// A type that inherits from to use for the provider values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexCollectionTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type conversionType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? providerComparerType)
+ => (ComplexCollectionTypePropertyBuilder)base.HasConversion(conversionType, comparerType, providerComparerType);
+}
diff --git a/src/EFCore/Metadata/IConventionTypeBase.cs b/src/EFCore/Metadata/IConventionTypeBase.cs
index 7e056624bdb..2e20cb235ee 100644
--- a/src/EFCore/Metadata/IConventionTypeBase.cs
+++ b/src/EFCore/Metadata/IConventionTypeBase.cs
@@ -37,12 +37,6 @@ public interface IConventionTypeBase : IReadOnlyTypeBase, IConventionAnnotatable
new IConventionEntityType ContainingEntityType
=> (IConventionEntityType)this;
- ///
- /// Gets this entity type or the closest collection property in the complex property chain.
- ///
- new IConventionTypeBase ContainingType
- => this;
-
///
/// Gets the base type of this type. Returns if this is not a derived type in an inheritance hierarchy.
///
diff --git a/src/EFCore/Metadata/IMutableTypeBase.cs b/src/EFCore/Metadata/IMutableTypeBase.cs
index 9c17325656e..69efecea0e5 100644
--- a/src/EFCore/Metadata/IMutableTypeBase.cs
+++ b/src/EFCore/Metadata/IMutableTypeBase.cs
@@ -32,12 +32,6 @@ public interface IMutableTypeBase : IReadOnlyTypeBase, IMutableAnnotatable
new IMutableEntityType ContainingEntityType
=> (IMutableEntityType)this;
- ///
- /// Gets this entity type or the closest collection property in the complex property chain.
- ///
- new IMutableTypeBase ContainingType
- => this;
-
///
/// Gets or sets the base type of this type. Returns if this is not a derived type in an inheritance
/// hierarchy.
diff --git a/src/EFCore/Metadata/IReadOnlyTypeBase.cs b/src/EFCore/Metadata/IReadOnlyTypeBase.cs
index f5d04a545d7..86fc3734eee 100644
--- a/src/EFCore/Metadata/IReadOnlyTypeBase.cs
+++ b/src/EFCore/Metadata/IReadOnlyTypeBase.cs
@@ -25,12 +25,6 @@ public interface IReadOnlyTypeBase : IReadOnlyAnnotatable
IReadOnlyEntityType ContainingEntityType
=> (IReadOnlyEntityType)this;
- ///
- /// Gets this entity type or the closest collection property in the complex property chain.
- ///
- IReadOnlyTypeBase ContainingType
- => this;
-
///
/// Gets the base type of this type. Returns if this is not a
/// derived type in an inheritance hierarchy.
diff --git a/src/EFCore/Metadata/ITypeBase.cs b/src/EFCore/Metadata/ITypeBase.cs
index b037a700f94..903d909e292 100644
--- a/src/EFCore/Metadata/ITypeBase.cs
+++ b/src/EFCore/Metadata/ITypeBase.cs
@@ -22,12 +22,6 @@ public interface ITypeBase : IReadOnlyTypeBase, IAnnotatable
new IEntityType ContainingEntityType
=> (IEntityType)this;
- ///
- /// Gets the containing entity type or the element type of the closest complex collection property in the complex property chain from the containing entity type.
- ///
- new ITypeBase ContainingType
- => this;
-
///
/// Gets the base type of this type. Returns if this is not a derived type in an inheritance
/// hierarchy.
diff --git a/src/EFCore/Metadata/Internal/ComplexType.cs b/src/EFCore/Metadata/Internal/ComplexType.cs
index b3929de7122..e380d2693c6 100644
--- a/src/EFCore/Metadata/Internal/ComplexType.cs
+++ b/src/EFCore/Metadata/Internal/ComplexType.cs
@@ -104,10 +104,10 @@ public virtual EntityType ContainingEntityType
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public virtual TypeBase ContainingType
+ public virtual TypeBase ContainingEntryType
=> ComplexProperty.DeclaringType switch
{
- ComplexType declaringComplexType when !declaringComplexType.ComplexProperty.IsCollection => declaringComplexType.ContainingType,
+ ComplexType declaringComplexType when !declaringComplexType.ComplexProperty.IsCollection => declaringComplexType.ContainingEntryType,
_ => ComplexProperty.DeclaringType
};
@@ -657,46 +657,10 @@ IEntityType ITypeBase.ContainingEntityType
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- IReadOnlyTypeBase IReadOnlyTypeBase.ContainingType
+ IRuntimeTypeBase IRuntimeTypeBase.ContainingEntryType
{
[DebuggerStepThrough]
- get => ContainingType;
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- IMutableTypeBase IMutableTypeBase.ContainingType
- {
- [DebuggerStepThrough]
- get => ContainingType;
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- IConventionTypeBase IConventionTypeBase.ContainingType
- {
- [DebuggerStepThrough]
- get => ContainingType;
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- ITypeBase ITypeBase.ContainingType
- {
- [DebuggerStepThrough]
- get => ContainingType;
+ get => ContainingEntryType;
}
///
diff --git a/src/EFCore/Metadata/Internal/IRuntimeTypeBase.cs b/src/EFCore/Metadata/Internal/IRuntimeTypeBase.cs
index e40fd042c1b..a7411e199bc 100644
--- a/src/EFCore/Metadata/Internal/IRuntimeTypeBase.cs
+++ b/src/EFCore/Metadata/Internal/IRuntimeTypeBase.cs
@@ -21,6 +21,18 @@ public interface IRuntimeTypeBase : ITypeBase
///
PropertyCounts CalculateCounts();
+ ///
+ /// Gets this entity type or the closest collection complex type in the complex property chain.
+ ///
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ IRuntimeTypeBase ContainingEntryType
+ => this;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs b/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs
index c2aef88f3fb..80abde1b829 100644
--- a/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs
+++ b/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs
@@ -43,9 +43,10 @@ public static T CheckContains(this IReadOnlyTypeBase structuralType, T proper
Check.NotNull(property);
return !property.DeclaringType.IsAssignableFrom(structuralType)
- && (!property.DeclaringType.ContainingType.IsAssignableFrom(structuralType)
+ && (!((IRuntimeTypeBase)property.DeclaringType).ContainingEntryType.IsAssignableFrom(structuralType)
|| (property.DeclaringType is IComplexType complexType
&& complexType.ComplexProperty.IsCollection))
+ && structuralType.ClrType != typeof(object) // For testing
? throw new InvalidOperationException(
CoreStrings.PropertyDoesNotBelong(property.Name, property.DeclaringType.DisplayName(), structuralType.DisplayName()))
: property;
diff --git a/src/EFCore/Metadata/RuntimeComplexType.cs b/src/EFCore/Metadata/RuntimeComplexType.cs
index f14b6d72d00..dd71a31f3fc 100644
--- a/src/EFCore/Metadata/RuntimeComplexType.cs
+++ b/src/EFCore/Metadata/RuntimeComplexType.cs
@@ -54,9 +54,9 @@ public RuntimeComplexType(
RuntimeComplexType declaringComplexType => declaringComplexType.ContainingEntityType,
_ => throw new NotImplementedException()
};
- ContainingType = ComplexProperty.DeclaringType switch
+ ContainingEntryType = ComplexProperty.DeclaringType switch
{
- RuntimeComplexType declaringComplexType when !declaringComplexType.ComplexProperty.IsCollection => declaringComplexType.ContainingType,
+ RuntimeComplexType declaringComplexType when !declaringComplexType.ComplexProperty.IsCollection => declaringComplexType.ContainingEntryType,
_ => ComplexProperty.DeclaringType
};
}
@@ -72,10 +72,9 @@ public RuntimeComplexType(
///
public virtual RuntimeComplexProperty ComplexProperty { get; }
-
private RuntimeEntityType ContainingEntityType { get; }
- private RuntimeTypeBase ContainingType { get; }
+ private RuntimeTypeBase ContainingEntryType { get; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -278,22 +277,10 @@ IEntityType ITypeBase.ContainingEntityType
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- IReadOnlyTypeBase IReadOnlyTypeBase.ContainingType
- {
- [DebuggerStepThrough]
- get => ContainingType;
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- ITypeBase ITypeBase.ContainingType
+ IRuntimeTypeBase IRuntimeTypeBase.ContainingEntryType
{
[DebuggerStepThrough]
- get => ContainingType;
+ get => ContainingEntryType;
}
///
diff --git a/test/EFCore.Cosmos.FunctionalTests/AddHocVectorSearchCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/AdHocVectorSearchCosmosTest.cs
similarity index 96%
rename from test/EFCore.Cosmos.FunctionalTests/AddHocVectorSearchCosmosTest.cs
rename to test/EFCore.Cosmos.FunctionalTests/AdHocVectorSearchCosmosTest.cs
index 0bbfcb49a35..4cbb276cc93 100644
--- a/test/EFCore.Cosmos.FunctionalTests/AddHocVectorSearchCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/AdHocVectorSearchCosmosTest.cs
@@ -7,7 +7,7 @@
namespace Microsoft.EntityFrameworkCore;
[CosmosCondition(CosmosCondition.DoesNotUseTokenCredential)]
-public class AddHocVectorSearchCosmosTest(NonSharedFixture fixture) : NonSharedModelTestBase(fixture), IClassFixture
+public class AdHocVectorSearchCosmosTest(NonSharedFixture fixture) : NonSharedModelTestBase(fixture), IClassFixture
{
protected override string StoreName
=> "AdHocVectorSearchTests";
diff --git a/test/EFCore.Cosmos.FunctionalTests/AddHocFullTextSearchCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/AddHocFullTextSearchCosmosTest.cs
index 93f36b59633..b8c9f8e1a53 100644
--- a/test/EFCore.Cosmos.FunctionalTests/AddHocFullTextSearchCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/AddHocFullTextSearchCosmosTest.cs
@@ -7,7 +7,7 @@
namespace Microsoft.EntityFrameworkCore;
[CosmosCondition(CosmosCondition.DoesNotUseTokenCredential)]
-public class AddHocFullTextSearchCosmosTest(NonSharedFixture fixture) : NonSharedModelTestBase(fixture), IClassFixture
+public class AdHocFullTextSearchCosmosTest(NonSharedFixture fixture) : NonSharedModelTestBase(fixture), IClassFixture
{
protected override string StoreName
=> "AdHocFullTextSearchTests";
@@ -162,34 +162,22 @@ public class Entity
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
- => modelBuilder.Entity(b =>
- {
- b.ToContainer("Entities").HasDefaultFullTextLanguage("xx-YY");
- b.HasPartitionKey(x => x.PartitionKey);
- b.Property(x => x.Name).EnableFullTextSearch();
- b.HasIndex(x => x.Name).IsFullTextIndex();
- });
+ {
+ modelBuilder.HasDefaultFullTextLanguage("xx-YY");
+ modelBuilder.Entity(b =>
+ {
+ b.ToContainer("Entities");
+ b.HasPartitionKey(x => x.PartitionKey);
+ b.Property(x => x.Name).EnableFullTextSearch();
+ b.HasIndex(x => x.Name).IsFullTextIndex();
+ });
+ }
}
#endregion
#region DefaultFullTextSearchLanguageMismatch
- [ConditionalFact]
- public async Task Set_different_full_text_search_default_language_for_the_same_container()
- {
- var message = (await Assert.ThrowsAsync(
- () => InitializeAsync())).Message;
-
- Assert.Equal(
- CosmosStrings.FullTextSearchDefaultLanguageMismatch(
- "pl-PL",
- nameof(ContextDefaultFullTextSearchLanguageMismatch.Entity1),
- nameof(ContextDefaultFullTextSearchLanguageMismatch.Entity2),
- "en-US",
- "Entities"), message);
- }
-
protected class ContextDefaultFullTextSearchLanguageMismatch(DbContextOptions options) : DbContext(options)
{
public DbSet Entities1 { get; set; } = null!;
@@ -213,9 +201,10 @@ public class Entity2
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
+ modelBuilder.HasDefaultFullTextLanguage("pl-PL");
modelBuilder.Entity(b =>
{
- b.ToContainer("Entities").HasDefaultFullTextLanguage("pl-PL");
+ b.ToContainer("Entities");
b.HasPartitionKey(x => x.PartitionKey);
b.Property(x => x.Name).EnableFullTextSearch();
b.HasIndex(x => x.Name).IsFullTextIndex();
@@ -223,7 +212,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity(b =>
{
- b.ToContainer("Entities").HasDefaultFullTextLanguage("en-US");
+ b.ToContainer("Entities");
b.HasPartitionKey(x => x.PartitionKey);
b.Property(x => x.Name).EnableFullTextSearch();
b.HasIndex(x => x.Name).IsFullTextIndex();
@@ -276,6 +265,7 @@ public class Entity3
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
+ modelBuilder.HasDefaultFullTextLanguage("xx-YY");
modelBuilder.Entity(b =>
{
b.ToContainer("Entities");
@@ -286,7 +276,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity(b =>
{
- b.ToContainer("Entities").HasDefaultFullTextLanguage("xx-YY");
+ b.ToContainer("Entities");
b.HasPartitionKey(x => x.PartitionKey);
b.Property(x => x.Name).EnableFullTextSearch();
b.HasIndex(x => x.Name).IsFullTextIndex();
@@ -328,13 +318,16 @@ public class Entity
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
- => modelBuilder.Entity(b =>
{
- b.ToContainer("Entities").HasDefaultFullTextLanguage("xx-YY");
- b.HasPartitionKey(x => x.PartitionKey);
- b.Property(x => x.Name).EnableFullTextSearch();
- b.HasIndex(x => x.Name).IsFullTextIndex();
- });
+ modelBuilder.HasDefaultFullTextLanguage("xx-YY");
+ modelBuilder.Entity(b =>
+ {
+ b.ToContainer("Entities");
+ b.HasPartitionKey(x => x.PartitionKey);
+ b.Property(x => x.Name).EnableFullTextSearch();
+ b.HasIndex(x => x.Name).IsFullTextIndex();
+ });
+ }
}
#endregion
@@ -363,13 +356,16 @@ public class Entity
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
- => modelBuilder.Entity(b =>
{
- b.ToContainer("Entities").HasDefaultFullTextLanguage("en-US");
- b.HasPartitionKey(x => x.PartitionKey);
- b.Property(x => x.Name).EnableFullTextSearch("xx-YY");
- b.HasIndex(x => x.Name).IsFullTextIndex();
- });
+ modelBuilder.HasDefaultFullTextLanguage("en-US");
+ modelBuilder.Entity(b =>
+ {
+ b.ToContainer("Entities");
+ b.HasPartitionKey(x => x.PartitionKey);
+ b.Property(x => x.Name).EnableFullTextSearch("xx-YY");
+ b.HasIndex(x => x.Name).IsFullTextIndex();
+ });
+ }
}
#endregion
diff --git a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs
index 4eca9446a7b..b3f236038e5 100644
--- a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs
@@ -843,11 +843,6 @@ public override void Can_set_complex_property_annotation()
Notes (List) Element type: string Required", complexCollection.ToDebugString(), ignoreLineEndingDifferences: true);
}
- [ConditionalFact(Skip = "Issue #31253: Complex type collections are not supported in Cosmos")]
- public override void Properties_can_be_set_to_generate_values_on_Add()
- {
- }
-
[ConditionalFact(Skip = "Issue #31253: Complex type collections are not supported in Cosmos")]
public override void Access_mode_can_be_overridden_at_entity_and_property_levels()
{
@@ -863,11 +858,6 @@ public override void Can_set_property_annotation()
{
}
- [ConditionalFact(Skip = "Issue #31253: Complex type collections are not supported in Cosmos")]
- public override void Properties_can_set_row_version()
- {
- }
-
[ConditionalFact(Skip = "Issue #31253: Complex type collections are not supported in Cosmos")]
public override void Can_set_property_annotation_by_type()
{
@@ -883,11 +873,6 @@ public override void Value_converter_configured_on_non_nullable_type_is_applied(
{
}
- [ConditionalFact(Skip = "Issue #31253: Complex type collections are not supported in Cosmos")]
- public override void Can_set_precision_and_scale_for_properties()
- {
- }
-
[ConditionalFact(Skip = "Issue #31253: Complex type collections are not supported in Cosmos")]
public override void Properties_can_be_ignored_by_type()
{
@@ -958,16 +943,6 @@ public override void Can_ignore_shadow_properties_when_they_have_been_added_expl
{
}
- [ConditionalFact(Skip = "Issue #31253: Complex type collections are not supported in Cosmos")]
- public override void Can_set_max_length_for_properties()
- {
- }
-
- [ConditionalFact(Skip = "Issue #31253: Complex type collections are not supported in Cosmos")]
- public override void Properties_can_be_made_concurrency_tokens()
- {
- }
-
[ConditionalFact(Skip = "Issue #31253: Complex type collections are not supported in Cosmos")]
public override void Properties_specified_by_string_are_shadow_properties_unless_already_known_to_be_CLR_properties()
{
diff --git a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs
index b088d2a608c..1aec9dff2ef 100644
--- a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs
@@ -435,6 +435,7 @@ await database.Value.GetCosmosDBSqlContainers().CreateOrUpdateAsync(
mappedTypes.Add(entityType);
}
+ var fullTextDefaultLanguage = model.GetDefaultFullTextSearchLanguage();
foreach (var (containerName, mappedTypes) in containers)
{
IReadOnlyList partitionKeyStoreNames = Array.Empty();
@@ -443,7 +444,6 @@ await database.Value.GetCosmosDBSqlContainers().CreateOrUpdateAsync(
ThroughputProperties? throughput = null;
var indexes = new List();
var vectors = new List<(IProperty Property, CosmosVectorType VectorType)>();
- string? fullTextDefaultLanguage = null;
var fullTextProperties = new List<(IProperty Property, string? Language)>();
foreach (var entityType in mappedTypes)
@@ -456,7 +456,6 @@ await database.Value.GetCosmosDBSqlContainers().CreateOrUpdateAsync(
analyticalTtl ??= entityType.GetAnalyticalStoreTimeToLive();
defaultTtl ??= entityType.GetDefaultTimeToLive();
throughput ??= entityType.GetThroughput();
- fullTextDefaultLanguage ??= entityType.GetDefaultFullTextSearchLanguage();
ProcessEntityType(entityType, indexes, vectors, fullTextProperties);
}
diff --git a/test/EFCore.InMemory.FunctionalTests/Scaffolding/Baselines/ComplexTypes/PrincipalDerivedEntityType.cs b/test/EFCore.InMemory.FunctionalTests/Scaffolding/Baselines/ComplexTypes/PrincipalDerivedEntityType.cs
index 94e409387ac..d47f4281dc8 100644
--- a/test/EFCore.InMemory.FunctionalTests/Scaffolding/Baselines/ComplexTypes/PrincipalDerivedEntityType.cs
+++ b/test/EFCore.InMemory.FunctionalTests/Scaffolding/Baselines/ComplexTypes/PrincipalDerivedEntityType.cs
@@ -93,10 +93,7 @@ public static RuntimeComplexProperty Create(RuntimeEntityType declaringType)
fieldInfo: typeof(CompiledModelTestBase.OwnedType).GetField("_details", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
propertyAccessMode: PropertyAccessMode.FieldDuringConstruction,
nullable: true,
- maxLength: 64,
unicode: false,
- precision: 3,
- scale: 2,
sentinel: "");
details.SetGetter(
string (CompiledModelTestBase.PrincipalDerived> entity, IReadOnlyList indices) => ((PrincipalDerivedUnsafeAccessors>.ManyOwned(entity) == null ? throw new InvalidOperationException(CoreStrings.ComplexCollectionNotInitialized("PrincipalDerived", "ManyOwned")) : PrincipalDerivedUnsafeAccessors>.ManyOwned(entity))[indices[0]] == null ? default(string) : (PrincipalDerivedUnsafeAccessors>.ManyOwned(entity) == null ? throw new InvalidOperationException(CoreStrings.ComplexCollectionNotInitialized("PrincipalDerived", "ManyOwned")) : PrincipalDerivedUnsafeAccessors>.ManyOwned(entity))[indices[0]].Details),
diff --git a/test/EFCore.Relational.Specification.Tests/ModelBuilding/RelationalTestModelBuilderExtensions.cs b/test/EFCore.Relational.Specification.Tests/ModelBuilding/RelationalTestModelBuilderExtensions.cs
index 813061231ef..c0feb06aa53 100644
--- a/test/EFCore.Relational.Specification.Tests/ModelBuilding/RelationalTestModelBuilderExtensions.cs
+++ b/test/EFCore.Relational.Specification.Tests/ModelBuilding/RelationalTestModelBuilderExtensions.cs
@@ -1646,6 +1646,23 @@ public static ModelBuilderTest.TestComplexTypePropertyBuilder HasJson
return builder;
}
+ public static ModelBuilderTest.TestComplexCollectionTypePropertyBuilder HasJsonPropertyName(
+ this ModelBuilderTest.TestComplexCollectionTypePropertyBuilder builder,
+ string? name)
+ {
+ switch (builder)
+ {
+ case IInfrastructure> genericBuilder:
+ genericBuilder.Instance.HasJsonPropertyName(name);
+ break;
+ case IInfrastructure nonGenericBuilder:
+ nonGenericBuilder.Instance.HasJsonPropertyName(name);
+ break;
+ }
+
+ return builder;
+ }
+
public static ModelBuilderTest.TestComplexTypePrimitiveCollectionBuilder HasJsonPropertyName(
this ModelBuilderTest.TestComplexTypePrimitiveCollectionBuilder builder,
string? name)
diff --git a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.ComplexCollections.cs b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.ComplexCollections.cs
index 5e1203ad118..f5782e88439 100644
--- a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.ComplexCollections.cs
+++ b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.ComplexCollections.cs
@@ -365,51 +365,6 @@ public virtual void Properties_specified_by_string_are_shadow_properties_unless_
Assert.NotEqual(complexType.FindProperty("Gluon").GetShadowIndex(), complexType.FindProperty("Photon").GetShadowIndex());
}
- [ConditionalFact]
- public virtual void Properties_can_be_made_concurrency_tokens()
- {
- var modelBuilder = CreateModelBuilder();
-
- modelBuilder
- .Ignore()
- .Ignore()
- .Entity()
- .Ignore(e => e.Quarks)
- .ComplexCollection(
- e => e.QuarksCollection,
- b =>
- {
- b.Property(e => e.Up).IsConcurrencyToken();
- b.Property(e => e.Down).IsConcurrencyToken(false);
- b.Property("Charm").IsConcurrencyToken();
- b.Property("Strange").IsConcurrencyToken(false);
- b.Property("Top").IsConcurrencyToken();
- b.Property("Bottom").IsConcurrencyToken(false);
- b.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotifications);
- });
-
- var model = modelBuilder.FinalizeModel();
- var complexType = model.FindEntityType(typeof(ComplexProperties)).GetComplexProperties().Single().ComplexType;
-
- Assert.False(complexType.FindProperty(Customer.IdProperty.Name).IsConcurrencyToken);
- Assert.True(complexType.FindProperty("Up").IsConcurrencyToken);
- Assert.False(complexType.FindProperty("Down").IsConcurrencyToken);
- Assert.True(complexType.FindProperty("Charm").IsConcurrencyToken);
- Assert.False(complexType.FindProperty("Strange").IsConcurrencyToken);
- Assert.True(complexType.FindProperty("Top").IsConcurrencyToken);
- Assert.False(complexType.FindProperty("Bottom").IsConcurrencyToken);
-
- Assert.Equal(-1, complexType.FindProperty(Customer.IdProperty.Name).GetOriginalValueIndex());
- Assert.Equal(2, complexType.FindProperty("Up").GetOriginalValueIndex());
- Assert.Equal(-1, complexType.FindProperty("Down").GetOriginalValueIndex());
- Assert.Equal(0, complexType.FindProperty("Charm").GetOriginalValueIndex());
- Assert.Equal(-1, complexType.FindProperty("Strange").GetOriginalValueIndex());
- Assert.Equal(1, complexType.FindProperty("Top").GetOriginalValueIndex());
- Assert.Equal(-1, complexType.FindProperty("Bottom").GetOriginalValueIndex());
-
- Assert.Equal(ChangeTrackingStrategy.ChangingAndChangedNotifications, complexType.GetChangeTrackingStrategy());
- }
-
[ConditionalFact]
public virtual void Properties_can_have_access_mode_set()
{
@@ -922,103 +877,6 @@ protected virtual void Throws_for_incompatible_type()
.ComplexCollection, Customer>(nameof(ComplexProperties.Customer))).Message);
}
- [ConditionalFact]
- public virtual void Properties_can_be_set_to_generate_values_on_Add()
- {
- var modelBuilder = CreateModelBuilder();
-
- modelBuilder
- .Ignore()
- .Ignore()
- .Entity()
- .ComplexCollection(
- e => e.QuarksCollection,
- b =>
- {
- b.Property(e => e.Up).ValueGeneratedOnAddOrUpdate();
- b.Property(e => e.Down).ValueGeneratedNever();
- b.Property("Charm").Metadata.ValueGenerated = ValueGenerated.OnUpdateSometimes;
- b.Property("Strange").ValueGeneratedNever();
- b.Property("Top").ValueGeneratedOnAddOrUpdate();
- b.Property("Bottom").ValueGeneratedOnUpdate();
- });
-
- var model = modelBuilder.FinalizeModel();
-
- var complexType = model.FindEntityType(typeof(ComplexProperties)).GetComplexProperties().Single().ComplexType;
- Assert.Equal(ValueGenerated.Never, complexType.FindProperty(Customer.IdProperty.Name).ValueGenerated);
- Assert.Equal(ValueGenerated.OnAddOrUpdate, complexType.FindProperty("Up").ValueGenerated);
- Assert.Equal(ValueGenerated.Never, complexType.FindProperty("Down").ValueGenerated);
- Assert.Equal(ValueGenerated.OnUpdateSometimes, complexType.FindProperty("Charm").ValueGenerated);
- Assert.Equal(ValueGenerated.Never, complexType.FindProperty("Strange").ValueGenerated);
- Assert.Equal(ValueGenerated.OnAddOrUpdate, complexType.FindProperty("Top").ValueGenerated);
- Assert.Equal(ValueGenerated.OnUpdate, complexType.FindProperty("Bottom").ValueGenerated);
- }
-
- [ConditionalFact]
- public virtual void Properties_can_set_row_version()
- {
- var modelBuilder = CreateModelBuilder();
-
- modelBuilder
- .Ignore()
- .Ignore()
- .Entity()
- .ComplexCollection(
- e => e.QuarksCollection,
- b =>
- {
- b.Property(e => e.Up).IsRowVersion();
- b.Property(e => e.Down).ValueGeneratedNever();
- b.Property("Charm").IsRowVersion();
- });
-
- var model = modelBuilder.FinalizeModel();
-
- var complexType = model.FindEntityType(typeof(ComplexProperties)).GetComplexProperties().Single().ComplexType;
-
- Assert.Equal(ValueGenerated.OnAddOrUpdate, complexType.FindProperty("Up").ValueGenerated);
- Assert.Equal(ValueGenerated.Never, complexType.FindProperty("Down").ValueGenerated);
- Assert.Equal(ValueGenerated.OnAddOrUpdate, complexType.FindProperty("Charm").ValueGenerated);
-
- Assert.True(complexType.FindProperty("Up").IsConcurrencyToken);
- Assert.False(complexType.FindProperty("Down").IsConcurrencyToken);
- Assert.True(complexType.FindProperty("Charm").IsConcurrencyToken);
- }
-
- [ConditionalFact]
- public virtual void Can_set_max_length_for_properties()
- {
- var modelBuilder = CreateModelBuilder();
-
- modelBuilder
- .Ignore()
- .Ignore()
- .Entity()
- .ComplexCollection(
- e => e.QuarksCollection,
- b =>
- {
- b.Property(e => e.Up).HasMaxLength(0);
- b.Property(e => e.Down).HasMaxLength(100);
- b.Property("Charm").HasMaxLength(0);
- b.Property("Strange").HasMaxLength(-1);
- b.Property("Top").HasMaxLength(0);
- b.Property("Bottom").HasMaxLength(100);
- });
-
- var model = modelBuilder.FinalizeModel();
- var complexType = model.FindEntityType(typeof(ComplexProperties)).GetComplexProperties().Single().ComplexType;
-
- Assert.Null(complexType.FindProperty(Customer.IdProperty.Name).GetMaxLength());
- Assert.Equal(0, complexType.FindProperty("Up").GetMaxLength());
- Assert.Equal(100, complexType.FindProperty("Down").GetMaxLength());
- Assert.Equal(0, complexType.FindProperty("Charm").GetMaxLength());
- Assert.Equal(-1, complexType.FindProperty("Strange").GetMaxLength());
- Assert.Equal(0, complexType.FindProperty("Top").GetMaxLength());
- Assert.Equal(100, complexType.FindProperty("Bottom").GetMaxLength());
- }
-
[ConditionalFact]
public virtual void Can_set_max_length_for_property_type()
{
@@ -1160,46 +1018,6 @@ public virtual void Can_set_unbounded_max_length_for_property_type()
Assert.Equal(-1, complexType.FindProperty("Bottom").GetMaxLength());
}
- [ConditionalFact]
- public virtual void Can_set_precision_and_scale_for_properties()
- {
- var modelBuilder = CreateModelBuilder();
-
- modelBuilder
- .Ignore()
- .Ignore()
- .Entity()
- .ComplexCollection(
- e => e.QuarksCollection,
- b =>
- {
- b.Property(e => e.Up).HasPrecision(1, 0);
- b.Property(e => e.Down).HasPrecision(100, 10);
- b.Property("Charm").HasPrecision(1, 0);
- b.Property("Strange").HasPrecision(100, 10);
- b.Property("Top").HasPrecision(1, 0);
- b.Property("Bottom").HasPrecision(100, 10);
- });
-
- var model = modelBuilder.FinalizeModel();
- var complexType = model.FindEntityType(typeof(ComplexProperties)).GetComplexProperties().Single().ComplexType;
-
- Assert.Null(complexType.FindProperty(Customer.IdProperty.Name).GetPrecision());
- Assert.Null(complexType.FindProperty(Customer.IdProperty.Name).GetScale());
- Assert.Equal(1, complexType.FindProperty("Up").GetPrecision());
- Assert.Equal(0, complexType.FindProperty("Up").GetScale());
- Assert.Equal(100, complexType.FindProperty("Down").GetPrecision());
- Assert.Equal(10, complexType.FindProperty("Down").GetScale());
- Assert.Equal(1, complexType.FindProperty("Charm").GetPrecision());
- Assert.Equal(0, complexType.FindProperty("Charm").GetScale());
- Assert.Equal(100, complexType.FindProperty("Strange").GetPrecision());
- Assert.Equal(10, complexType.FindProperty("Strange").GetScale());
- Assert.Equal(1, complexType.FindProperty("Top").GetPrecision());
- Assert.Equal(0, complexType.FindProperty("Top").GetScale());
- Assert.Equal(100, complexType.FindProperty("Bottom").GetPrecision());
- Assert.Equal(10, complexType.FindProperty("Bottom").GetScale());
- }
-
[ConditionalFact]
public virtual void Can_set_precision_and_scale_for_property_type()
{
@@ -1427,15 +1245,8 @@ public virtual void PropertyBuilder_methods_can_be_chained()
.Property(e => e.Up)
.IsRequired()
.HasAnnotation("A", "V")
- .IsConcurrencyToken()
- .ValueGeneratedNever()
- .ValueGeneratedOnAdd()
- .ValueGeneratedOnAddOrUpdate()
- .ValueGeneratedOnUpdate()
.IsUnicode()
- .HasMaxLength(100)
.HasSentinel(1)
- .HasPrecision(10, 1)
.HasValueGenerator()
.HasValueGenerator(typeof(CustomValueGenerator))
.HasValueGeneratorFactory()
diff --git a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.Generic.cs b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.Generic.cs
index 332ec7fcbcd..b3ddcf9b4a2 100644
--- a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.Generic.cs
+++ b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.Generic.cs
@@ -589,6 +589,9 @@ protected virtual GenericTestComplexPropertyBuilder Wrap(ComplexPropertyBu
protected virtual GenericTestComplexTypePropertyBuilder Wrap(ComplexTypePropertyBuilder propertyBuilder)
=> new(propertyBuilder);
+ protected virtual GenericTestComplexCollectionTypePropertyBuilder Wrap(ComplexCollectionTypePropertyBuilder propertyBuilder)
+ => new(propertyBuilder);
+
protected virtual GenericTestComplexTypePrimitiveCollectionBuilder Wrap(
ComplexTypePrimitiveCollectionBuilder propertyBuilder)
=> new(propertyBuilder);
@@ -599,12 +602,12 @@ public override TestComplexCollectionBuilder HasPropertyAnnotation(str
public override TestComplexCollectionBuilder HasTypeAnnotation(string annotation, object? value)
=> Wrap(PropertyBuilder.HasTypeAnnotation(annotation, value));
- public override TestComplexTypePropertyBuilder Property(
+ public override TestComplexCollectionTypePropertyBuilder Property(
Expression> propertyExpression)
where TProperty : default
=> Wrap(PropertyBuilder.Property(propertyExpression));
- public override TestComplexTypePropertyBuilder Property(string propertyName)
+ public override TestComplexCollectionTypePropertyBuilder Property(string propertyName)
=> Wrap(PropertyBuilder.Property(propertyName));
public override TestComplexTypePrimitiveCollectionBuilder PrimitiveCollection(
@@ -615,7 +618,7 @@ public override TestComplexTypePrimitiveCollectionBuilder PrimitiveCo
public override TestComplexTypePrimitiveCollectionBuilder PrimitiveCollection(string propertyName)
=> Wrap(PropertyBuilder.PrimitiveCollection(propertyName));
- public override TestComplexTypePropertyBuilder IndexerProperty(string propertyName)
+ public override TestComplexCollectionTypePropertyBuilder IndexerProperty(string propertyName)
=> Wrap(PropertyBuilder.IndexerProperty(propertyName));
public override TestComplexPropertyBuilder ComplexProperty(string propertyName)
@@ -1141,6 +1144,125 @@ ComplexTypePropertyBuilder IInfrastructure PropertyBuilder;
}
+ protected class GenericTestComplexCollectionTypePropertyBuilder(ComplexCollectionTypePropertyBuilder propertyBuilder) :
+ TestComplexCollectionTypePropertyBuilder,
+ IInfrastructure>
+ {
+ protected ComplexCollectionTypePropertyBuilder PropertyBuilder { get; } = propertyBuilder;
+
+ public override IMutableProperty Metadata
+ => PropertyBuilder.Metadata;
+
+ protected virtual TestComplexCollectionTypePropertyBuilder Wrap(ComplexCollectionTypePropertyBuilder propertyBuilder)
+ => new GenericTestComplexCollectionTypePropertyBuilder(propertyBuilder);
+
+ public override TestComplexCollectionTypePropertyBuilder HasAnnotation(string annotation, object? value)
+ => Wrap(PropertyBuilder.HasAnnotation(annotation, value));
+
+ public override TestComplexCollectionTypePropertyBuilder IsRequired(bool isRequired = true)
+ => Wrap(PropertyBuilder.IsRequired(isRequired));
+
+ public override TestComplexCollectionTypePropertyBuilder HasSentinel(TProperty? sentinel)
+ => Wrap(PropertyBuilder.HasSentinel(sentinel));
+
+ public override TestComplexCollectionTypePropertyBuilder IsUnicode(bool unicode = true)
+ => Wrap(PropertyBuilder.IsUnicode(unicode));
+
+ public override TestComplexCollectionTypePropertyBuilder HasValueGenerator()
+ => Wrap(PropertyBuilder.HasValueGenerator());
+
+ public override TestComplexCollectionTypePropertyBuilder HasValueGenerator(Type valueGeneratorType)
+ => Wrap(PropertyBuilder.HasValueGenerator(valueGeneratorType));
+
+ public override TestComplexCollectionTypePropertyBuilder HasValueGeneratorFactory()
+ => Wrap(PropertyBuilder.HasValueGeneratorFactory());
+
+ public override TestComplexCollectionTypePropertyBuilder