diff --git a/EFCore.Runtime.slnf b/EFCore.Runtime.slnf
index 504bb19e161..d0953712259 100644
--- a/EFCore.Runtime.slnf
+++ b/EFCore.Runtime.slnf
@@ -9,6 +9,8 @@
"src\\EFCore.InMemory\\EFCore.InMemory.csproj",
"src\\EFCore.Proxies\\EFCore.Proxies.csproj",
"src\\EFCore.Relational\\EFCore.Relational.csproj",
+ "src\\EFCore.SqlServer.Abstractions\\EFCore.SqlServer.Abstractions.csproj",
+ "src\\EFCore.SqlServer.HierarchyId\\EFCore.SqlServer.HierarchyId.csproj",
"src\\EFCore.SqlServer.NTS\\EFCore.SqlServer.NTS.csproj",
"src\\EFCore.SqlServer\\EFCore.SqlServer.csproj",
"src\\EFCore.Sqlite.Core\\EFCore.Sqlite.Core.csproj",
@@ -33,6 +35,7 @@
"test\\EFCore.Relational.Tests\\EFCore.Relational.Tests.csproj",
"test\\EFCore.Specification.Tests\\EFCore.Specification.Tests.csproj",
"test\\EFCore.SqlServer.FunctionalTests\\EFCore.SqlServer.FunctionalTests.csproj",
+ "test\\EFCore.SqlServer.HierarchyId.Tests\\EFCore.SqlServer.HierarchyId.Tests.csproj",
"test\\EFCore.SqlServer.Tests\\EFCore.SqlServer.Tests.csproj",
"test\\EFCore.Sqlite.FunctionalTests\\EFCore.Sqlite.FunctionalTests.csproj",
"test\\EFCore.Sqlite.Tests\\EFCore.Sqlite.Tests.csproj",
diff --git a/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs
index e3a33708622..e1bf8acccc4 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.Cosmos.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore;
@@ -26,11 +27,11 @@ public static string GetJsonPropertyName(this IReadOnlyProperty property)
private static string GetDefaultJsonPropertyName(IReadOnlyProperty property)
{
- var entityType = property.DeclaringEntityType;
- var ownership = entityType.FindOwnership();
+ var entityType = property.DeclaringType as IEntityType;
+ var ownership = entityType?.FindOwnership();
if (ownership != null
- && !entityType.IsDocumentRoot())
+ && !entityType!.IsDocumentRoot())
{
var pk = property.FindContainingPrimaryKey();
if (pk != null
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosValueGenerationConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosValueGenerationConvention.cs
index 64828e5aa56..3a7836693c1 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosValueGenerationConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosValueGenerationConvention.cs
@@ -67,9 +67,10 @@ public virtual void ProcessEntityTypeAnnotationChanged(
/// The store value generation strategy to set for the given property.
protected override ValueGenerated? GetValueGenerated(IConventionProperty property)
{
- var entityType = property.DeclaringEntityType;
+ var entityType = property.DeclaringType as IConventionEntityType;
var propertyType = property.ClrType.UnwrapNullableType();
- if (propertyType == typeof(int))
+ if (propertyType == typeof(int)
+ && entityType != null)
{
var ownership = entityType.FindOwnership();
if (ownership is { IsUnique: false }
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/StoreKeyConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/StoreKeyConvention.cs
index 7bd0c030bc0..4dc7cc621e2 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/StoreKeyConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/StoreKeyConvention.cs
@@ -295,9 +295,9 @@ public virtual void ProcessPropertyAnnotationChanged(
&& (string?)annotation?.Value == IdPropertyJsonName
&& propertyBuilder.Metadata.Name != DefaultIdPropertyName)
{
- var entityType = propertyBuilder.Metadata.DeclaringEntityType;
+ var declaringType = propertyBuilder.Metadata.DeclaringType;
- var idProperty = entityType.FindProperty(DefaultIdPropertyName);
+ var idProperty = declaringType.FindProperty(DefaultIdPropertyName);
if (idProperty != null)
{
foreach (var key in idProperty.GetContainingKeys().ToList())
@@ -306,7 +306,7 @@ public virtual void ProcessPropertyAnnotationChanged(
}
}
- ProcessIdProperty(entityType.Builder);
+ ProcessIdProperty(declaringType.FundamentalEntityType.Builder);
}
}
}
diff --git a/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs b/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs
index a115200db60..331e414bce5 100644
--- a/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs
+++ b/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs
@@ -20,7 +20,7 @@ public static class CosmosPropertyExtensions
public static bool IsOrdinalKeyProperty(this IReadOnlyProperty property)
{
Check.DebugAssert(
- property.DeclaringEntityType.IsOwned(), $"Expected {property.DeclaringEntityType.DisplayName()} to be owned.");
+ (property.DeclaringType as IEntityType)?.IsOwned() == true, $"Expected {property.DeclaringType.DisplayName()} to be owned.");
Check.DebugAssert(property.GetJsonPropertyName().Length == 0, $"Expected {property.Name} to be non-persisted.");
return property.FindContainingPrimaryKey() is IReadOnlyKey { Properties.Count: > 1 }
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
index 620034dee62..67d7e7da9d1 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
@@ -598,11 +598,13 @@ private Expression CreateGetValueExpression(
var storeName = property.GetJsonPropertyName();
if (storeName.Length == 0)
{
- var entityType = property.DeclaringEntityType;
- if (!entityType.IsDocumentRoot())
+ var entityType = property.DeclaringType as IEntityType;
+ if (entityType == null
+ || !entityType.IsDocumentRoot())
{
- var ownership = entityType.FindOwnership();
- if (!ownership.IsUnique
+ var ownership = entityType?.FindOwnership();
+ if (ownership != null
+ && !ownership.IsUnique
&& property.IsOrdinalKeyProperty())
{
var readExpression = _ordinalParameterBindings[jObjectExpression];
@@ -621,8 +623,8 @@ private Expression CreateGetValueExpression(
if (_ownerMappings.TryGetValue(jObjectExpression, out var ownerInfo))
{
Check.DebugAssert(
- principalProperty.DeclaringEntityType.IsAssignableFrom(ownerInfo.EntityType),
- $"{principalProperty.DeclaringEntityType} is not assignable from {ownerInfo.EntityType}");
+ principalProperty.DeclaringType.IsAssignableFrom(ownerInfo.EntityType),
+ $"{principalProperty.DeclaringType} is not assignable from {ownerInfo.EntityType}");
ownerJObjectExpression = ownerInfo.JObjectExpression;
}
diff --git a/src/EFCore.Cosmos/Query/Internal/EntityProjectionExpression.cs b/src/EFCore.Cosmos/Query/Internal/EntityProjectionExpression.cs
index cc9eb884b41..abcd196ab7d 100644
--- a/src/EFCore.Cosmos/Query/Internal/EntityProjectionExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/EntityProjectionExpression.cs
@@ -101,8 +101,8 @@ public virtual Expression Update(Expression accessExpression)
///
public virtual Expression BindProperty(IProperty property, bool clientEval)
{
- if (!EntityType.IsAssignableFrom(property.DeclaringEntityType)
- && !property.DeclaringEntityType.IsAssignableFrom(EntityType))
+ if (!EntityType.IsAssignableFrom(property.DeclaringType)
+ && !property.DeclaringType.IsAssignableFrom(EntityType))
{
throw new InvalidOperationException(
CosmosStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName()));
diff --git a/src/EFCore.Cosmos/ValueGeneration/IdValueGeneratorFactory.cs b/src/EFCore.Cosmos/ValueGeneration/IdValueGeneratorFactory.cs
index bf4207a07e9..5b1236478cf 100644
--- a/src/EFCore.Cosmos/ValueGeneration/IdValueGeneratorFactory.cs
+++ b/src/EFCore.Cosmos/ValueGeneration/IdValueGeneratorFactory.cs
@@ -15,6 +15,6 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.ValueGeneration;
public class IdValueGeneratorFactory : ValueGeneratorFactory
{
///
- public override ValueGenerator Create(IProperty property, IEntityType entityType)
+ public override ValueGenerator Create(IProperty property, ITypeBase entityType)
=> new IdValueGenerator();
}
diff --git a/src/EFCore.Cosmos/ValueGeneration/Internal/CosmosValueGeneratorSelector.cs b/src/EFCore.Cosmos/ValueGeneration/Internal/CosmosValueGeneratorSelector.cs
index d11d7fa3943..afad6d61dc0 100644
--- a/src/EFCore.Cosmos/ValueGeneration/Internal/CosmosValueGeneratorSelector.cs
+++ b/src/EFCore.Cosmos/ValueGeneration/Internal/CosmosValueGeneratorSelector.cs
@@ -28,14 +28,14 @@ public CosmosValueGeneratorSelector(ValueGeneratorSelectorDependencies dependenc
/// 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.
///
- protected override ValueGenerator? FindForType(IProperty property, IEntityType entityType, Type clrType)
+ protected override ValueGenerator? FindForType(IProperty property, ITypeBase typeBase, Type clrType)
{
if (property.GetJsonPropertyName() == ""
&& clrType == typeof(int))
{
- return new TemporaryNumberValueGeneratorFactory().Create(property, entityType);
+ return new TemporaryNumberValueGeneratorFactory().Create(property, typeBase);
}
- return base.FindForType(property, entityType, clrType);
+ return base.FindForType(property, typeBase, clrType);
}
}
diff --git a/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs b/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs
index edda96f37d2..a53268548e6 100644
--- a/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs
+++ b/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs
@@ -185,7 +185,7 @@ private static void UpdateOwnedTypes(IMutableEntityType entityType)
if (oldProperty is IConventionProperty conventionProperty
&& conventionProperty.GetConfigurationSource() == ConfigurationSource.Convention)
{
- oldProperty.DeclaringEntityType.RemoveProperty(oldProperty);
+ oldProperty.DeclaringType.RemoveProperty(oldProperty);
}
}
}
diff --git a/src/EFCore.Design/Properties/DesignStrings.Designer.cs b/src/EFCore.Design/Properties/DesignStrings.Designer.cs
index ebbade0a075..815834efe07 100644
--- a/src/EFCore.Design/Properties/DesignStrings.Designer.cs
+++ b/src/EFCore.Design/Properties/DesignStrings.Designer.cs
@@ -666,7 +666,7 @@ public static string RevertMigration(object? name)
name);
///
- /// To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
+ /// To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
///
public static string SensitiveInformationWarning
=> GetString("SensitiveInformationWarning");
@@ -829,3 +829,4 @@ private static string GetString(string name, params string[] formatterNames)
}
}
}
+
diff --git a/src/EFCore.Design/Properties/DesignStrings.resx b/src/EFCore.Design/Properties/DesignStrings.resx
index 7a692251456..5da17b71cb6 100644
--- a/src/EFCore.Design/Properties/DesignStrings.resx
+++ b/src/EFCore.Design/Properties/DesignStrings.resx
@@ -127,10 +127,10 @@
Entity Framework Core Migrations Bundle
- Unable to create a 'DbContext' of type '{contextType}'. The exception '{rootException}' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
+ Unable to create a 'DbContext' of type '{contextType}'. The exception '{rootException}' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
- The exception '{rootException}' was thrown while attempting to find 'DbContext' types. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
+ The exception '{rootException}' was thrown while attempting to find 'DbContext' types. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
Unable to find expected assembly attribute [DesignTimeProviderServices] in provider assembly '{runtimeProviderAssemblyName}'. This attribute is required to identify the class which acts as the design-time service provider factory for the provider.
@@ -381,7 +381,7 @@ Change your target project to the migrations project by using the Package Manage
The migration '{name}' has already been applied to the database. Revert it and try again. If the migration has been applied to other databases, consider reverting its changes using a new migration instead.
- To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
+ To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
Localize the URL if we have localized docs.
@@ -441,4 +441,4 @@ Change your target project to the migrations project by using the Package Manage
Writing model snapshot to '{file}'.
-
+
\ No newline at end of file
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
index 88fe47c7bab..79dd3634758 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
@@ -96,7 +96,7 @@ public virtual string TransformText()
if (!Options.SuppressConnectionStringWarning)
{
- this.Write(@"#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
+ this.Write(@"#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
");
}
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt
index 843dc271f94..80e3c9eca95 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt
@@ -75,7 +75,7 @@ public partial class <#= Options.ContextName #> : DbContext
if (!Options.SuppressConnectionStringWarning)
{
#>
-#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
+#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
<#
}
#>
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
index 1f9f21fe697..36a1bbc2f65 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
@@ -4,6 +4,7 @@
using System.Text;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Internal;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Microsoft.EntityFrameworkCore.Scaffolding.Internal;
@@ -459,6 +460,11 @@ private string GenerateEntityType(IEntityType entityType, string @namespace, str
{
CreateEntityType(entityType, mainBuilder, methodBuilder, namespaces, className, nullable);
+ foreach (var complexProperty in entityType.GetDeclaredComplexProperties())
+ {
+ CreateComplexProperty(complexProperty, mainBuilder, methodBuilder, namespaces, className, nullable);
+ }
+
var foreignKeyNumber = 1;
foreach (var foreignKey in entityType.GetDeclaredForeignKeys())
{
@@ -538,6 +544,17 @@ private void CreateEntityType(
Create(property, parameters);
}
+ foreach (var complexProperty in entityType.GetDeclaredComplexProperties())
+ {
+ mainBuilder
+ .Append(_code.Identifier(complexProperty.Name, capitalize: true))
+ .Append("ComplexProperty")
+ .Append(".Create")
+ .Append("(")
+ .Append(entityTypeVariable)
+ .AppendLine(");");
+ }
+
foreach (var key in entityType.GetDeclaredKeys())
{
Create(key, propertyVariables, parameters, nullable);
@@ -665,6 +682,25 @@ private void Create(
IProperty property,
Dictionary propertyVariables,
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
+ {
+ var variableName = _code.Identifier(property.Name, parameters.ScopeVariables, capitalize: false);
+ propertyVariables[property] = variableName;
+
+ Create(property, variableName, propertyVariables, parameters);
+
+ CreateAnnotations(
+ property,
+ _annotationCodeGenerator.Generate,
+ parameters with { TargetName = variableName });
+
+ parameters.MainBuilder.AppendLine();
+ }
+
+ private void Create(
+ IProperty property,
+ string variableName,
+ Dictionary propertyVariables,
+ CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
{
var valueGeneratorFactoryType = (Type?)property[CoreAnnotationNames.ValueGeneratorFactoryType];
if (valueGeneratorFactoryType == null
@@ -672,7 +708,7 @@ private void Create(
{
throw new InvalidOperationException(
DesignStrings.CompiledModelValueGenerator(
- property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasValueGeneratorFactory)));
+ property.DeclaringType.ShortName(), property.Name, nameof(PropertyBuilder.HasValueGeneratorFactory)));
}
var valueComparerType = (Type?)property[CoreAnnotationNames.ValueComparerType];
@@ -681,7 +717,7 @@ private void Create(
{
throw new InvalidOperationException(
DesignStrings.CompiledModelValueComparer(
- property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
+ property.DeclaringType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
}
var providerValueComparerType = (Type?)property[CoreAnnotationNames.ProviderValueComparerType];
@@ -690,7 +726,7 @@ private void Create(
{
throw new InvalidOperationException(
DesignStrings.CompiledModelValueComparer(
- property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
+ property.DeclaringType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
}
var valueConverterType = GetValueConverterType(property);
@@ -699,7 +735,7 @@ private void Create(
{
throw new InvalidOperationException(
DesignStrings.CompiledModelValueConverter(
- property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
+ property.DeclaringType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
}
if (property is IConventionProperty conventionProperty
@@ -707,12 +743,9 @@ private void Create(
{
throw new InvalidOperationException(
DesignStrings.CompiledModelTypeMapping(
- property.DeclaringEntityType.ShortName(), property.Name, "Customize()", parameters.ClassName));
+ property.DeclaringType.ShortName(), property.Name, "Customize()", parameters.ClassName));
}
- var variableName = _code.Identifier(property.Name, parameters.ScopeVariables, capitalize: false);
- propertyVariables[property] = variableName;
-
var mainBuilder = parameters.MainBuilder;
mainBuilder
.Append("var ").Append(variableName).Append(" = ").Append(parameters.TargetName).AppendLine(".AddProperty(")
@@ -870,13 +903,6 @@ private void Create(
mainBuilder
.AppendLine(");")
.DecrementIndent();
-
- CreateAnnotations(
- property,
- _annotationCodeGenerator.Generate,
- parameters with { TargetName = variableName });
-
- mainBuilder.AppendLine();
}
private static Type? GetValueConverterType(IProperty property)
@@ -925,9 +951,8 @@ private void Create(
}
return i == ForeignKey.LongestFkChainAllowedLength
- ? throw new InvalidOperationException(
- CoreStrings.RelationshipCycle(
- property.DeclaringEntityType.DisplayName(), property.Name, "ValueConverterType"))
+ ? throw new InvalidOperationException(CoreStrings.RelationshipCycle(
+ property.DeclaringType.DisplayName(), property.Name, "ValueConverterType"))
: null;
}
@@ -1057,9 +1082,10 @@ private void Create(
PropertyBaseParameters(property, parameters, skipType: true);
+ AddNamespace(property.ClrType, parameters.Namespaces);
mainBuilder
.AppendLine(",")
- .Append("serviceType: typeof(" + property.ClrType.DisplayName(fullName: true, compilable: true) + ")");
+ .Append("serviceType: typeof(" + _code.Reference(property.ClrType) + ")");
mainBuilder
.AppendLine(");")
@@ -1148,6 +1174,166 @@ private void Create(
mainBuilder.AppendLine();
}
+ private void CreateComplexProperty(
+ IComplexProperty complexProperty,
+ IndentedStringBuilder mainBuilder,
+ IndentedStringBuilder methodBuilder,
+ SortedSet namespaces,
+ string topClassName,
+ bool nullable)
+ {
+ mainBuilder
+ .AppendLine()
+ .Append("private static class ")
+ .Append(_code.Identifier(complexProperty.Name, capitalize: true))
+ .AppendLine("ComplexProperty")
+ .AppendLine("{");
+
+ var complexType = complexProperty.ComplexType;
+ using (mainBuilder.Indent())
+ {
+ var declaringTypeVariable = "declaringType";
+ mainBuilder
+ .Append("public static RuntimeComplexProperty Create(")
+ .Append(complexProperty.DeclaringType is IEntityType ? "RuntimeEntityType " : "RuntimeComplexType ")
+ .Append(declaringTypeVariable)
+ .AppendLine(")")
+ .AppendLine("{");
+
+ using (mainBuilder.Indent())
+ {
+ const string complexPropertyVariable = "complexProperty";
+ const string complexTypeVariable = "complexType";
+ var variables = new HashSet
+ {
+ declaringTypeVariable,
+ complexPropertyVariable,
+ complexTypeVariable
+ };
+
+ mainBuilder
+ .Append("var ").Append(complexPropertyVariable).Append(" = ")
+ .Append(declaringTypeVariable).Append(".AddComplexProperty(")
+ .IncrementIndent()
+ .Append(_code.Literal(complexProperty.Name))
+ .AppendLine(",")
+ .Append(_code.Literal(complexProperty.ClrType))
+ .AppendLine(",")
+ .Append(_code.Literal(complexType.Name))
+ .AppendLine(",")
+ .Append(_code.Literal(complexType.ClrType));
+
+ AddNamespace(complexProperty.ClrType, namespaces);
+ AddNamespace(complexType.ClrType, namespaces);
+
+ var parameters = new CSharpRuntimeAnnotationCodeGeneratorParameters(
+ declaringTypeVariable,
+ topClassName,
+ mainBuilder,
+ methodBuilder,
+ namespaces,
+ variables,
+ nullable);
+
+ PropertyBaseParameters(complexProperty, parameters, skipType: true);
+
+ if (complexProperty.IsNullable)
+ {
+ mainBuilder.AppendLine(",")
+ .Append("nullable: ")
+ .Append(_code.Literal(true));
+ }
+
+ if (complexProperty.IsCollection)
+ {
+ mainBuilder.AppendLine(",")
+ .Append("collection: ")
+ .Append(_code.Literal(true));
+ }
+
+ var changeTrackingStrategy = complexType.GetChangeTrackingStrategy();
+ if (changeTrackingStrategy != ChangeTrackingStrategy.Snapshot)
+ {
+ namespaces.Add(typeof(ChangeTrackingStrategy).Namespace!);
+
+ mainBuilder.AppendLine(",")
+ .Append("changeTrackingStrategy: ")
+ .Append(_code.Literal(changeTrackingStrategy));
+ }
+
+ var indexerPropertyInfo = complexType.FindIndexerPropertyInfo();
+ if (indexerPropertyInfo != null)
+ {
+ mainBuilder.AppendLine(",")
+ .Append("indexerPropertyInfo: RuntimeEntityType.FindIndexerProperty(")
+ .Append(_code.Literal(complexType.ClrType))
+ .Append(")");
+ }
+
+ if (complexType.IsPropertyBag)
+ {
+ mainBuilder.AppendLine(",")
+ .Append("propertyBag: ")
+ .Append(_code.Literal(true));
+ }
+
+ mainBuilder
+ .AppendLine(");")
+ .AppendLine()
+ .DecrementIndent();
+
+ mainBuilder
+ .Append("var ").Append(complexTypeVariable).Append(" = ")
+ .Append(complexPropertyVariable).AppendLine(".ComplexType;");
+
+ var complexTypeParameters = parameters with { TargetName = complexTypeVariable };
+ var propertyVariables = new Dictionary();
+ foreach (var property in complexType.GetProperties())
+ {
+ Create(property, propertyVariables, complexTypeParameters);
+ }
+
+ foreach (var nestedComplexProperty in complexType.GetComplexProperties())
+ {
+ mainBuilder
+ .Append(_code.Identifier(nestedComplexProperty.Name, capitalize: true))
+ .Append("ComplexProperty")
+ .Append(".Create")
+ .Append("(")
+ .Append(complexTypeVariable)
+ .AppendLine(");");
+ }
+
+ CreateAnnotations(
+ complexType,
+ _annotationCodeGenerator.Generate,
+ complexTypeParameters);
+
+ CreateAnnotations(
+ complexProperty,
+ _annotationCodeGenerator.Generate,
+ parameters with { TargetName = complexPropertyVariable });
+
+ mainBuilder
+ .Append("return ")
+ .Append(complexPropertyVariable)
+ .AppendLine(";");
+ }
+
+ mainBuilder.AppendLine("}");
+ }
+
+ using (mainBuilder.Indent())
+ {
+ foreach (var nestedComplexProperty in complexType.GetComplexProperties())
+ {
+ CreateComplexProperty(nestedComplexProperty, mainBuilder, methodBuilder, namespaces, topClassName, nullable);
+ }
+ }
+
+ mainBuilder.AppendLine("}");
+ }
+
private void CreateForeignKey(
IForeignKey foreignKey,
int foreignKeyNumber,
diff --git a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs
index 40032248f1c..80143d3243c 100644
--- a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs
@@ -714,10 +714,10 @@ protected virtual ModelBuilder VisitForeignKeys(
uniquifier: NavigationUniquifier);
var leftSkipNavigation = leftEntityType.AddSkipNavigation(
- leftNavigationPropertyName, null, rightEntityType, collection: true, onDependent: false);
+ leftNavigationPropertyName, memberInfo: null, targetEntityType: rightEntityType, collection: true, onDependent: false);
leftSkipNavigation.SetForeignKey(fks[0]);
var rightSkipNavigation = rightEntityType.AddSkipNavigation(
- rightNavigationPropertyName, null, leftEntityType, collection: true, onDependent: false);
+ rightNavigationPropertyName, memberInfo: null, targetEntityType: leftEntityType, collection: true, onDependent: false);
rightSkipNavigation.SetForeignKey(fks[1]);
leftSkipNavigation.SetInverse(rightSkipNavigation);
rightSkipNavigation.SetInverse(leftSkipNavigation);
diff --git a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs
index 1b972d62a46..83f0de02464 100644
--- a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs
+++ b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs
@@ -159,7 +159,7 @@ public static EventDefinition LogSavedChanges(IDiagnosticsLogger logger)
}
///
- /// Transactions are not supported by the in-memory store. See http://go.microsoft.com/fwlink/?LinkId=800142
+ /// Transactions are not supported by the in-memory store. See https://go.microsoft.com/fwlink/?LinkId=800142
///
public static EventDefinition LogTransactionsNotSupported(IDiagnosticsLogger logger)
{
diff --git a/src/EFCore.InMemory/Properties/InMemoryStrings.resx b/src/EFCore.InMemory/Properties/InMemoryStrings.resx
index a5fda0181c4..93e3f5cfd5d 100644
--- a/src/EFCore.InMemory/Properties/InMemoryStrings.resx
+++ b/src/EFCore.InMemory/Properties/InMemoryStrings.resx
@@ -131,7 +131,7 @@
Information InMemoryEventId.ChangesSaved int
- Transactions are not supported by the in-memory store. See http://go.microsoft.com/fwlink/?LinkId=800142
+ Transactions are not supported by the in-memory store. See https://go.microsoft.com/fwlink/?LinkId=800142
Warning InMemoryEventId.TransactionIgnoredWarning
diff --git a/src/EFCore.InMemory/Query/Internal/EntityProjectionExpression.cs b/src/EFCore.InMemory/Query/Internal/EntityProjectionExpression.cs
index c2d461e8d43..39587ddf366 100644
--- a/src/EFCore.InMemory/Query/Internal/EntityProjectionExpression.cs
+++ b/src/EFCore.InMemory/Query/Internal/EntityProjectionExpression.cs
@@ -74,8 +74,8 @@ public virtual EntityProjectionExpression UpdateEntityType(IEntityType derivedTy
var readExpressionMap = new Dictionary();
foreach (var (property, methodCallExpression) in _readExpressionMap)
{
- if (derivedType.IsAssignableFrom(property.DeclaringEntityType)
- || property.DeclaringEntityType.IsAssignableFrom(derivedType))
+ if (derivedType.IsAssignableFrom(property.DeclaringType)
+ || property.DeclaringType.IsAssignableFrom(derivedType))
{
readExpressionMap[property] = methodCallExpression;
}
@@ -92,8 +92,16 @@ public virtual EntityProjectionExpression UpdateEntityType(IEntityType derivedTy
///
public virtual MethodCallExpression BindProperty(IProperty property)
{
- if (!EntityType.IsAssignableFrom(property.DeclaringEntityType)
- && !property.DeclaringEntityType.IsAssignableFrom(EntityType))
+ if (property.DeclaringType is not IEntityType entityType)
+ {
+ if (EntityType != property.DeclaringType)
+ {
+ throw new InvalidOperationException(
+ InMemoryStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName()));
+ }
+ }
+ else if (!EntityType.IsAssignableFrom(entityType)
+ && !entityType.IsAssignableFrom(EntityType))
{
throw new InvalidOperationException(
InMemoryStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName()));
diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs
index 42631c8db87..d6d001dc697 100644
--- a/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs
+++ b/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs
@@ -40,13 +40,12 @@ public InMemoryStore(IInMemoryTableFactory tableFactory)
public virtual InMemoryIntegerValueGenerator GetIntegerValueGenerator(
IProperty property)
{
+ var entityType = property.DeclaringType.FundamentalEntityType;
lock (_lock)
{
- var entityType = property.DeclaringEntityType;
-
return EnsureTable(entityType).GetIntegerValueGenerator(
property,
- entityType.GetDerivedTypesInclusive().Select(type => EnsureTable(type)).ToArray());
+ entityType.GetDerivedTypesInclusive().Select(EnsureTable).ToArray());
}
}
diff --git a/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryValueGeneratorSelector.cs b/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryValueGeneratorSelector.cs
index 8b98f3f12a3..d2147ac2bb5 100644
--- a/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryValueGeneratorSelector.cs
+++ b/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryValueGeneratorSelector.cs
@@ -35,12 +35,12 @@ public InMemoryValueGeneratorSelector(
/// 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 override ValueGenerator Select(IProperty property, IEntityType entityType)
+ public override ValueGenerator Select(IProperty property, ITypeBase typeBase)
=> property.GetValueGeneratorFactory() == null
&& property.ClrType.IsInteger()
&& property.ClrType.UnwrapNullableType() != typeof(char)
? GetOrCreate(property)
- : base.Select(property, entityType);
+ : base.Select(property, typeBase);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -58,73 +58,57 @@ private ValueGenerator GetOrCreate(IProperty property)
throw new ArgumentException(
CoreStrings.InvalidValueGeneratorFactoryProperty(
- "InMemoryIntegerValueGeneratorFactory", property.Name, property.DeclaringEntityType.DisplayName()));
+ "InMemoryIntegerValueGeneratorFactory", property.Name, property.DeclaringType.DisplayName()));
}
private bool FindGenerator(IProperty property, Type type, out ValueGenerator? valueGenerator)
{
if (type == typeof(long))
{
- {
- valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
- return true;
- }
+ valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
+ return true;
}
if (type == typeof(int))
{
- {
- valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
- return true;
- }
+ valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
+ return true;
}
if (type == typeof(short))
{
- {
- valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
- return true;
- }
+ valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
+ return true;
}
if (type == typeof(byte))
{
- {
- valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
- return true;
- }
+ valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
+ return true;
}
if (type == typeof(ulong))
{
- {
- valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
- return true;
- }
+ valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
+ return true;
}
if (type == typeof(uint))
{
- {
- valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
- return true;
- }
+ valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
+ return true;
}
if (type == typeof(ushort))
{
- {
- valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
- return true;
- }
+ valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
+ return true;
}
if (type == typeof(sbyte))
{
- {
- valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
- return true;
- }
+ valueGenerator = _inMemoryStore.GetIntegerValueGenerator(property);
+ return true;
}
valueGenerator = null;
@@ -132,8 +116,8 @@ private bool FindGenerator(IProperty property, Type type, out ValueGenerator? va
}
///
- protected override ValueGenerator? FindForType(IProperty property, IEntityType entityType, Type clrType)
+ protected override ValueGenerator? FindForType(IProperty property, ITypeBase typeBase, Type clrType)
=> property.ValueGenerated != ValueGenerated.Never && FindGenerator(property, clrType, out var valueGenerator)
? valueGenerator!
- : base.FindForType(property, entityType, clrType);
+ : base.FindForType(property, typeBase, clrType);
}
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
index e39d8aef907..ac1c95e6b20 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
@@ -2469,7 +2469,7 @@ public static void ModelValidationKeyDefaultValueWarning(
if (diagnostics.ShouldLog(definition))
{
- definition.Log(diagnostics, property.Name, property.DeclaringEntityType.DisplayName());
+ definition.Log(diagnostics, property.Name, property.DeclaringType.DisplayName());
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -2489,7 +2489,7 @@ private static string ModelValidationKeyDefaultValueWarning(EventDefinitionBase
var p = (PropertyEventData)payload;
return d.GenerateMessage(
p.Property.Name,
- p.Property.DeclaringEntityType.DisplayName());
+ p.Property.DeclaringType.DisplayName());
}
///
@@ -2510,7 +2510,7 @@ public static void BoolWithDefaultWarning(
diagnostics,
property.ClrType.ShortDisplayName(),
property.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
defaultValue == null ? "null" : defaultValue.ToString()!,
property.ClrType.ShortDisplayName());
}
@@ -2534,7 +2534,7 @@ private static string BoolWithDefaultWarning(EventDefinitionBase definition, Eve
return d.GenerateMessage(
p.Property.ClrType.ShortDisplayName(),
p.Property.Name,
- p.Property.DeclaringEntityType.DisplayName(),
+ p.Property.DeclaringType.DisplayName(),
defaultValue == null ? "null" : defaultValue.ToString()!,
p.Property.ClrType.ShortDisplayName());
}
@@ -3077,7 +3077,7 @@ public static void TpcStoreGeneratedIdentityWarning(
{
definition.Log(
diagnostics,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name);
}
@@ -3097,7 +3097,7 @@ private static string TpcStoreGeneratedIdentity(EventDefinitionBase definition,
var d = (EventDefinition)definition;
var p = (PropertyEventData)payload;
return d.GenerateMessage(
- p.Property.DeclaringEntityType.DisplayName(),
+ p.Property.DeclaringType.DisplayName(),
p.Property.Name);
}
diff --git a/src/EFCore.Relational/Extensions/RelationalComplexTypePropertyBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalComplexTypePropertyBuilderExtensions.cs
new file mode 100644
index 00000000000..e2fe7925290
--- /dev/null
+++ b/src/EFCore.Relational/Extensions/RelationalComplexTypePropertyBuilderExtensions.cs
@@ -0,0 +1,521 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+// 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 RelationalComplexTypePropertyBuilderExtensions
+{
+ ///
+ /// Configures the column that the property maps to when targeting a relational database.
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The name of the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasColumnName(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? name)
+ {
+ Check.NullButNotEmpty(name, nameof(name));
+
+ propertyBuilder.Metadata.SetColumnName(name);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the column that the property maps to when targeting a relational database.
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The name of the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasColumnName(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? name)
+ => (ComplexTypePropertyBuilder)HasColumnName((ComplexTypePropertyBuilder)propertyBuilder, name);
+
+ ///
+ /// Configures the order of the column the property is mapped to.
+ ///
+ /// The builder of the property being configured.
+ /// The column order.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasColumnOrder(this ComplexTypePropertyBuilder propertyBuilder, int? order)
+ {
+ propertyBuilder.Metadata.SetColumnOrder(order);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the order of the column the property is mapped to.
+ ///
+ /// The builder of the property being configured.
+ /// The column order.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasColumnOrder(this ComplexTypePropertyBuilder propertyBuilder, int? order)
+ => (ComplexTypePropertyBuilder)HasColumnOrder((ComplexTypePropertyBuilder)propertyBuilder, order);
+
+ ///
+ /// Configures the data type of the column that the property maps to when targeting a relational database.
+ /// This should be the complete type name, including precision, scale, length, etc.
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The name of the data type of the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasColumnType(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? typeName)
+ {
+ Check.NullButNotEmpty(typeName, nameof(typeName));
+
+ propertyBuilder.Metadata.SetColumnType(typeName);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the data type of the column that the property maps to when targeting a relational database.
+ /// This should be the complete type name, including precision, scale, length, etc.
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The name of the data type of the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasColumnType(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? typeName)
+ => (ComplexTypePropertyBuilder)HasColumnType((ComplexTypePropertyBuilder)propertyBuilder, typeName);
+
+ ///
+ /// Configures the property as capable of storing only fixed-length data, such as strings.
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// A value indicating whether the property is constrained to fixed length values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public static ComplexTypePropertyBuilder IsFixedLength(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ bool fixedLength = true)
+ {
+ propertyBuilder.Metadata.SetIsFixedLength(fixedLength);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the property as capable of storing only fixed-length data, such as strings.
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// A value indicating whether the property is constrained to fixed length values.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public static ComplexTypePropertyBuilder IsFixedLength(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ bool fixedLength = true)
+ => (ComplexTypePropertyBuilder)IsFixedLength((ComplexTypePropertyBuilder)propertyBuilder, fixedLength);
+
+ ///
+ /// Configures the default value expression for the column that the property maps to when targeting a
+ /// relational database.
+ ///
+ ///
+ ///
+ /// When called with no argument, this method tells EF that a column has a default value constraint of
+ /// some sort without needing to specify exactly what it is. This can be useful when mapping EF to an
+ /// existing database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ ///
+ /// The builder for the property being configured.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasDefaultValueSql(this ComplexTypePropertyBuilder propertyBuilder)
+ {
+ propertyBuilder.Metadata.SetDefaultValueSql(string.Empty);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the default value expression for the column that the property maps to when targeting a relational database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The SQL expression for the default value of the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasDefaultValueSql(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? sql)
+ {
+ Check.NullButNotEmpty(sql, nameof(sql));
+
+ propertyBuilder.Metadata.SetDefaultValueSql(sql);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the default value expression for the column that the property maps to when targeting a
+ /// relational database.
+ ///
+ ///
+ ///
+ /// When called with no argument, this method tells EF that a column has a default value constraint of
+ /// some sort without needing to specify exactly what it is. This can be useful when mapping EF to an
+ /// existing database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasDefaultValueSql(
+ this ComplexTypePropertyBuilder propertyBuilder)
+ => (ComplexTypePropertyBuilder)HasDefaultValueSql((ComplexTypePropertyBuilder)propertyBuilder);
+
+ ///
+ /// Configures the default value expression for the column that the property maps to when targeting a relational database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The SQL expression for the default value of the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasDefaultValueSql(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? sql)
+ => (ComplexTypePropertyBuilder)HasDefaultValueSql((ComplexTypePropertyBuilder)propertyBuilder, sql);
+
+ ///
+ /// Configures the property to map to a computed column when targeting a relational database.
+ ///
+ ///
+ ///
+ /// When called with no arguments, this method tells EF that a column is computed without needing to
+ /// specify the actual SQL used to computed it. This can be useful when mapping EF to an existing
+ /// database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ ///
+ /// The builder for the property being configured.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasComputedColumnSql(this ComplexTypePropertyBuilder propertyBuilder)
+ {
+ propertyBuilder.Metadata.SetComputedColumnSql(string.Empty);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the property to map to a computed column when targeting a relational database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The SQL expression that computes values for the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasComputedColumnSql(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? sql)
+ => HasComputedColumnSql(propertyBuilder, sql, null);
+
+ ///
+ /// Configures the property to map to a computed column when targeting a relational database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The SQL expression that computes values for the column.
+ ///
+ /// If , the computed value is calculated on row modification and stored in the database like a regular column.
+ /// If , the value is computed when the value is read, and does not occupy any actual storage.
+ /// selects the database provider default.
+ ///
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasComputedColumnSql(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? sql,
+ bool? stored)
+ {
+ Check.NullButNotEmpty(sql, nameof(sql));
+
+ propertyBuilder.Metadata.SetComputedColumnSql(sql);
+
+ if (stored != null)
+ {
+ propertyBuilder.Metadata.SetIsStored(stored);
+ }
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the property to map to a computed column when targeting a relational database.
+ ///
+ ///
+ ///
+ /// When called with no arguments, this method tells EF that a column is computed without needing to
+ /// specify the actual SQL used to computed it. This can be useful when mapping EF to an existing
+ /// database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasComputedColumnSql(
+ this ComplexTypePropertyBuilder propertyBuilder)
+ => (ComplexTypePropertyBuilder)HasComputedColumnSql((ComplexTypePropertyBuilder)propertyBuilder);
+
+ ///
+ /// Configures the property to map to a computed column when targeting a relational database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The SQL expression that computes values for the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasComputedColumnSql(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? sql)
+ => HasComputedColumnSql(propertyBuilder, sql, null);
+
+ ///
+ /// Configures the property to map to a computed column when targeting a relational database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The SQL expression that computes values for the column.
+ ///
+ /// If , the computed value is calculated on row modification and stored in the database like a regular column.
+ /// If , the value is computed when the value is read, and does not occupy any actual storage.
+ /// selects the database provider default.
+ ///
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasComputedColumnSql(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? sql,
+ bool? stored)
+ => (ComplexTypePropertyBuilder)HasComputedColumnSql((ComplexTypePropertyBuilder)propertyBuilder, sql, stored);
+
+ ///
+ /// Configures the default value for the column that the property maps
+ /// to when targeting a relational database.
+ ///
+ ///
+ ///
+ /// When called with no argument, this method tells EF that a column has a default
+ /// value constraint of some sort without needing to specify exactly what it is.
+ /// This can be useful when mapping EF to an existing database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ ///
+ /// The builder for the property being configured.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasDefaultValue(this ComplexTypePropertyBuilder propertyBuilder)
+ {
+ propertyBuilder.Metadata.SetDefaultValue(DBNull.Value);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the default value for the column that the property maps
+ /// to when targeting a relational database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The default value of the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasDefaultValue(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ object? value)
+ {
+ propertyBuilder.Metadata.SetDefaultValue(value);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the default value for the column that the property maps
+ /// to when targeting a relational database.
+ ///
+ ///
+ ///
+ /// When called with no argument, this method tells EF that a column has a default
+ /// value constraint of some sort without needing to specify exactly what it is.
+ /// This can be useful when mapping EF to an existing database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasDefaultValue(
+ this ComplexTypePropertyBuilder propertyBuilder)
+ => (ComplexTypePropertyBuilder)HasDefaultValue((ComplexTypePropertyBuilder)propertyBuilder);
+
+ ///
+ /// Configures the default value for the column that the property maps
+ /// to when targeting a relational database.
+ ///
+ ///
+ /// See Database default values for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The default value of the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasDefaultValue(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ object? value)
+ => (ComplexTypePropertyBuilder)HasDefaultValue((ComplexTypePropertyBuilder)propertyBuilder, value);
+
+ ///
+ /// Configures a comment to be applied to the column
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The comment for the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasComment(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? comment)
+ {
+ propertyBuilder.Metadata.SetComment(comment);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures a comment to be applied to the column
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The comment for the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasComment(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? comment)
+ => (ComplexTypePropertyBuilder)HasComment((ComplexTypePropertyBuilder)propertyBuilder, comment);
+
+ ///
+ /// Configures the property to use the given collation. The database column will be created with the given
+ /// collation, and it will be used implicitly in all collation-sensitive operations.
+ ///
+ ///
+ /// See Database collations for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The collation for the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseCollation(this ComplexTypePropertyBuilder propertyBuilder, string? collation)
+ {
+ Check.NullButNotEmpty(collation, nameof(collation));
+
+ propertyBuilder.Metadata.SetCollation(collation);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the property to use the given collation. The database column will be created with the given
+ /// collation, and it will be used implicitly in all collation-sensitive operations.
+ ///
+ ///
+ /// See Database collations for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The collation for the column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseCollation(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? collation)
+ => (ComplexTypePropertyBuilder)UseCollation((ComplexTypePropertyBuilder)propertyBuilder, collation);
+
+ ///
+ /// 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 ComplexTypePropertyBuilder HasJsonPropertyName(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? name)
+ {
+ Check.NullButNotEmpty(name, nameof(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 ComplexTypePropertyBuilder HasJsonPropertyName(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? name)
+ => (ComplexTypePropertyBuilder)HasJsonPropertyName((ComplexTypePropertyBuilder)propertyBuilder, name);
+}
diff --git a/src/EFCore.Relational/Extensions/RelationalDbFunctionsExtensions.cs b/src/EFCore.Relational/Extensions/RelationalDbFunctionExtensions.cs
similarity index 97%
rename from src/EFCore.Relational/Extensions/RelationalDbFunctionsExtensions.cs
rename to src/EFCore.Relational/Extensions/RelationalDbFunctionExtensions.cs
index f3a3f86be73..7ce5591e770 100644
--- a/src/EFCore.Relational/Extensions/RelationalDbFunctionsExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalDbFunctionExtensions.cs
@@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore;
///
/// See Database functions for more information and examples.
///
-public static class RelationalDbFunctionsExtensions
+public static class RelationalDbFunctionExtensions
{
///
/// Explicitly specifies a collation to be used in a LINQ query. Can be used to generate fragments such as
diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
index f07f43709ef..80205f65e62 100644
--- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
@@ -248,26 +248,6 @@ public static void SetSchema(this IMutableEntityType entityType, string? value)
return (string.IsNullOrEmpty(schema) ? "" : schema + ".") + viewName;
}
- ///
- /// Returns the default mappings that the entity type would use.
- ///
- /// The entity type to get the table mappings for.
- /// The tables to which the entity type is mapped.
- public static IEnumerable GetDefaultMappings(this IEntityType entityType)
- => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.DefaultMappings)
- ?? Enumerable.Empty();
-
- ///
- /// Returns the tables to which the entity type is mapped.
- ///
- /// The entity type to get the table mappings for.
- /// The tables to which the entity type is mapped.
- public static IEnumerable GetTableMappings(this IEntityType entityType)
- => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.TableMappings)
- ?? Enumerable.Empty();
-
#endregion Table mapping
#region View mapping
@@ -418,16 +398,6 @@ public static void SetViewSchema(this IMutableEntityType entityType, string? val
=> entityType.FindAnnotation(RelationalAnnotationNames.ViewSchema)
?.GetConfigurationSource();
- ///
- /// Returns the views to which the entity type is mapped.
- ///
- /// The entity type to get the view mappings for.
- /// The views to which the entity type is mapped.
- public static IEnumerable GetViewMappings(this IEntityType entityType)
- => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.ViewMappings)
- ?? Enumerable.Empty();
-
#endregion View mapping
#region SQL query mapping
@@ -487,16 +457,6 @@ public static void SetSqlQuery(this IMutableEntityType entityType, string? name)
=> entityType.FindAnnotation(RelationalAnnotationNames.SqlQuery)
?.GetConfigurationSource();
- ///
- /// Returns the SQL string mappings.
- ///
- /// The entity type to get the SQL string mappings for.
- /// The SQL string to which the entity type is mapped.
- public static IEnumerable GetSqlQueryMappings(this IEntityType entityType)
- => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.SqlQueryMappings)
- ?? Enumerable.Empty();
-
#endregion SQL query mapping
#region Function mapping
@@ -547,16 +507,6 @@ public static void SetFunctionName(this IMutableEntityType entityType, string? n
=> entityType.FindAnnotation(RelationalAnnotationNames.FunctionName)
?.GetConfigurationSource();
- ///
- /// Returns the functions to which the entity type is mapped.
- ///
- /// The entity type to get the function mappings for.
- /// The functions to which the entity type is mapped.
- public static IEnumerable GetFunctionMappings(this IEntityType entityType)
- => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.FunctionMappings)
- ?? Enumerable.Empty();
-
#endregion
#region SProc mapping
@@ -798,36 +748,6 @@ public static IMutableStoredProcedure SetUpdateStoredProcedure(this IMutableEnti
public static ConfigurationSource? GetUpdateStoredProcedureConfigurationSource(this IConventionEntityType entityType)
=> StoredProcedure.GetStoredProcedureConfigurationSource(entityType, StoreObjectType.UpdateStoredProcedure);
- ///
- /// Returns the insert stored procedures to which the entity type is mapped.
- ///
- /// The entity type.
- /// The insert stored procedures to which the entity type is mapped.
- public static IEnumerable GetInsertStoredProcedureMappings(this IEntityType entityType)
- => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.InsertStoredProcedureMappings)
- ?? Enumerable.Empty();
-
- ///
- /// Returns the delete stored procedures to which the entity type is mapped.
- ///
- /// The entity type.
- /// The delete stored procedures to which the entity type is mapped.
- public static IEnumerable GetDeleteStoredProcedureMappings(this IEntityType entityType)
- => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.DeleteStoredProcedureMappings)
- ?? Enumerable.Empty();
-
- ///
- /// Returns the update stored procedures to which the entity type is mapped.
- ///
- /// The entity type.
- /// The update stored procedures to which the entity type is mapped.
- public static IEnumerable GetUpdateStoredProcedureMappings(this IEntityType entityType)
- => (IEnumerable?)entityType.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.UpdateStoredProcedureMappings)
- ?? Enumerable.Empty();
-
#endregion
#region Check constraint
@@ -1572,6 +1492,10 @@ public static void SetIsTableExcludedFromMigrations(
in StoreObjectIdentifier storeObject)
=> entityType.FindMappingFragment(storeObject)?.GetIsTableExcludedFromMigrationsConfigurationSource();
+ #endregion IsTableExcludedFromMigrations
+
+ #region Mapping strategy
+
///
/// Gets the mapping strategy for the derived types.
///
@@ -1587,10 +1511,6 @@ public static void SetIsTableExcludedFromMigrations(
? null
: RelationalAnnotationNames.TptMappingStrategy);
- #endregion IsTableExcludedFromMigrations
-
- #region Mapping strategy
-
///
/// Sets the mapping strategy for the derived types.
///
diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
index 5696676dbad..f7e3d5affc4 100644
--- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
@@ -63,13 +63,13 @@ public static string GetColumnName(this IReadOnlyProperty property)
if (property.IsPrimaryKey())
{
var tableFound = false;
- if (property.DeclaringEntityType.FindMappingFragment(storeObject) != null)
+ if (property.DeclaringType.FindMappingFragment(storeObject) != null)
{
tableFound = true;
}
- else
+ else if(property.DeclaringType is IReadOnlyEntityType declaringEntityType)
{
- foreach (var containingType in property.DeclaringEntityType.GetDerivedTypesInclusive())
+ foreach (var containingType in declaringEntityType.GetDerivedTypesInclusive())
{
if (StoreObjectIdentifier.Create(containingType, storeObject.StoreObjectType) == storeObject)
{
@@ -84,53 +84,57 @@ public static string GetColumnName(this IReadOnlyProperty property)
return null;
}
}
- else if (property.DeclaringEntityType.GetMappingStrategy() != RelationalAnnotationNames.TpcMappingStrategy)
+ else
{
- var declaringStoreObject = StoreObjectIdentifier.Create(property.DeclaringEntityType, storeObject.StoreObjectType);
- if (declaringStoreObject == null)
+ var declaringEntityType = property.DeclaringType.FundamentalEntityType;
+ if (declaringEntityType.GetMappingStrategy() != RelationalAnnotationNames.TpcMappingStrategy)
{
- var tableFound = false;
- var queue = new Queue();
- queue.Enqueue(property.DeclaringEntityType);
- while (queue.Count > 0 && !tableFound)
+ var declaringStoreObject = StoreObjectIdentifier.Create(property.DeclaringType, storeObject.StoreObjectType);
+ if (declaringStoreObject == null)
{
- foreach (var containingType in queue.Dequeue().GetDirectlyDerivedTypes())
+ var tableFound = false;
+ var queue = new Queue();
+ queue.Enqueue(declaringEntityType);
+ while (queue.Count > 0 && !tableFound)
{
- declaringStoreObject = StoreObjectIdentifier.Create(containingType, storeObject.StoreObjectType);
- if (declaringStoreObject == null)
+ foreach (var containingType in queue.Dequeue().GetDirectlyDerivedTypes())
{
- queue.Enqueue(containingType);
- continue;
- }
-
- if (declaringStoreObject == storeObject)
- {
- tableFound = true;
- break;
+ declaringStoreObject = StoreObjectIdentifier.Create(containingType, storeObject.StoreObjectType);
+ if (declaringStoreObject == null)
+ {
+ queue.Enqueue(containingType);
+ continue;
+ }
+
+ if (declaringStoreObject == storeObject)
+ {
+ tableFound = true;
+ break;
+ }
}
}
- }
- if (!tableFound)
- {
- return null;
- }
- }
- else
- {
- var fragments = property.DeclaringEntityType.GetMappingFragments(storeObject.StoreObjectType).ToList();
- if (fragments.Count > 0)
- {
- if (overrides == null
- && (declaringStoreObject != storeObject
- || fragments.Any(f => property.FindOverrides(f.StoreObject) != null)))
+ if (!tableFound)
{
return null;
}
}
- else if (declaringStoreObject != storeObject)
+ else
{
- return null;
+ var fragments = property.DeclaringType.GetMappingFragments(storeObject.StoreObjectType).ToList();
+ if (fragments.Count > 0)
+ {
+ if (overrides == null
+ && (declaringStoreObject != storeObject
+ || fragments.Any(f => property.FindOverrides(f.StoreObject) != null)))
+ {
+ return null;
+ }
+ }
+ else if (declaringStoreObject != storeObject)
+ {
+ return null;
+ }
}
}
}
@@ -181,7 +185,7 @@ public static string GetDefaultColumnName(this IReadOnlyProperty property)
}
}
- return Uniquifier.Truncate(name, property.DeclaringEntityType.Model.GetMaxIdentifierLength());
+ return Uniquifier.Truncate(name, property.DeclaringType.Model.GetMaxIdentifierLength());
}
///
@@ -192,7 +196,7 @@ public static string GetDefaultColumnName(this IReadOnlyProperty property)
/// The default column name to which the property would be mapped.
public static string? GetDefaultColumnName(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
{
- if (property.DeclaringEntityType.IsMappedToJson())
+ if (property.DeclaringType.IsMappedToJson())
{
return null;
}
@@ -209,30 +213,47 @@ public static string GetDefaultColumnName(this IReadOnlyProperty property)
return sharedTablePrincipalConcurrencyProperty.GetColumnName(storeObject)!;
}
- var entityType = property.DeclaringEntityType;
+
StringBuilder? builder = null;
var currentStoreObject = storeObject;
- while (true)
+ if (property.DeclaringType is IReadOnlyEntityType entityType)
{
- var ownership = entityType.GetForeignKeys().SingleOrDefault(fk => fk.IsOwnership);
- if (ownership == null)
+ while (true)
{
- break;
- }
+ var ownership = entityType.GetForeignKeys().SingleOrDefault(fk => fk.IsOwnership);
+ if (ownership == null)
+ {
+ break;
+ }
- var ownerType = ownership.PrincipalEntityType;
- if (StoreObjectIdentifier.Create(ownerType, currentStoreObject.StoreObjectType) != currentStoreObject
- && ownerType.GetMappingFragments(storeObject.StoreObjectType)
- .All(f => f.StoreObject != currentStoreObject))
- {
- break;
- }
+ var ownerType = ownership.PrincipalEntityType;
+ if (StoreObjectIdentifier.Create(ownerType, currentStoreObject.StoreObjectType) != currentStoreObject
+ && ownerType.GetMappingFragments(storeObject.StoreObjectType)
+ .All(f => f.StoreObject != currentStoreObject))
+ {
+ break;
+ }
+
+ builder ??= new StringBuilder();
+ builder.Insert(0, "_");
+ builder.Insert(0, ownership.PrincipalToDependent!.Name);
+ entityType = ownerType;
+ }
+ }
+ else if (StoreObjectIdentifier.Create(property.DeclaringType, currentStoreObject.StoreObjectType) == currentStoreObject
+ || property.DeclaringType.GetMappingFragments(storeObject.StoreObjectType)
+ .Any(f => f.StoreObject == currentStoreObject))
+ {
+ var complexType = (IReadOnlyComplexType)property.DeclaringType;
builder ??= new StringBuilder();
+ while (complexType != null)
+ {
+ builder.Insert(0, "_");
+ builder.Insert(0, complexType.ComplexProperty.Name);
- builder.Insert(0, "_");
- builder.Insert(0, ownership.PrincipalToDependent!.Name);
- entityType = ownerType;
+ complexType = complexType.ComplexProperty.DeclaringType as IReadOnlyComplexType;
+ }
}
var baseName = storeObject.StoreObjectType == StoreObjectType.Table ? property.GetDefaultColumnName() : property.Name;
@@ -244,7 +265,7 @@ public static string GetDefaultColumnName(this IReadOnlyProperty property)
builder.Append(baseName);
baseName = builder.ToString();
- return Uniquifier.Truncate(baseName, property.DeclaringEntityType.Model.GetMaxIdentifierLength());
+ return Uniquifier.Truncate(baseName, property.DeclaringType.Model.GetMaxIdentifierLength());
}
///
@@ -960,7 +981,7 @@ public static void SetDefaultValue(this IMutableProperty property, object? value
{
throw new InvalidOperationException(
RelationalStrings.IncorrectDefaultValueType(
- value, valueType, property.Name, property.ClrType, property.DeclaringEntityType.DisplayName()));
+ value, valueType, property.Name, property.ClrType, property.DeclaringType.DisplayName()));
}
}
@@ -1123,7 +1144,9 @@ public static void SetIsFixedLength(this IMutableProperty property, bool? fixedL
/// if the mapped column is nullable; otherwise.
public static bool IsColumnNullable(this IReadOnlyProperty property)
=> property.IsNullable
- || (property.DeclaringEntityType.BaseType != null && property.DeclaringEntityType.FindDiscriminatorProperty() != null);
+ || (property.DeclaringType is IReadOnlyEntityType entityType
+ && entityType.BaseType != null
+ && entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy);
///
/// Checks whether the column mapped to the given property will be nullable
@@ -1151,8 +1174,10 @@ public static bool IsColumnNullable(this IReadOnlyProperty property, in StoreObj
}
return property.IsNullable
- || (property.DeclaringEntityType.BaseType != null && property.DeclaringEntityType.FindDiscriminatorProperty() != null)
- || IsOptionalSharingDependent(property.DeclaringEntityType, storeObject, 0);
+ || (property.DeclaringType is IReadOnlyEntityType entityType
+ && ((entityType.BaseType != null
+ && entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy)
+ || IsOptionalSharingDependent(entityType, storeObject, 0)));
}
private static bool IsOptionalSharingDependent(
@@ -1403,7 +1428,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping(this IReadOnlyPrope
private static IReadOnlyProperty? FindSharedObjectRootProperty(IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
{
- if (property.DeclaringEntityType.IsMappedToJson())
+ if (property.DeclaringType.IsMappedToJson())
{
//JSON-splitting is not supported
//issue #28574
@@ -1415,7 +1440,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping(this IReadOnlyPrope
{
throw new InvalidOperationException(
RelationalStrings.PropertyNotMappedToTable(
- property.Name, property.DeclaringEntityType.DisplayName(), storeObject.DisplayName()));
+ property.Name, property.DeclaringType.DisplayName(), storeObject.DisplayName()));
}
var rootProperty = property;
@@ -1424,8 +1449,14 @@ public static RelationalTypeMapping GetRelationalTypeMapping(this IReadOnlyPrope
// Using a hashset is detrimental to the perf when there are no cycles
for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
{
+ var entityType = rootProperty.DeclaringType as IReadOnlyEntityType;
+ if (entityType == null)
+ {
+ break;
+ }
+
IReadOnlyProperty? linkedProperty = null;
- foreach (var p in rootProperty.DeclaringEntityType
+ foreach (var p in entityType
.FindRowInternalForeignKeys(storeObject)
.SelectMany(fk => fk.PrincipalEntityType.GetProperties()))
{
@@ -1462,9 +1493,8 @@ public static RelationalTypeMapping GetRelationalTypeMapping(this IReadOnlyPrope
// Using a hashset is detrimental to the perf when there are no cycles
for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
{
- var linkingRelationship = principalProperty.DeclaringEntityType
- .FindRowInternalForeignKeys(storeObject).FirstOrDefault();
-
+ var entityType = principalProperty.DeclaringType as IReadOnlyEntityType;
+ var linkingRelationship = entityType?.FindRowInternalForeignKeys(storeObject).FirstOrDefault();
if (linkingRelationship == null)
{
break;
@@ -1486,12 +1516,13 @@ public static RelationalTypeMapping GetRelationalTypeMapping(this IReadOnlyPrope
}
var principalProperty = property;
+
// Limit traversal to avoid getting stuck in a cycle (validation will throw for these later)
// Using a hashset is detrimental to the perf when there are no cycles
for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
{
- var linkingRelationship = principalProperty.DeclaringEntityType
- .FindRowInternalForeignKeys(storeObject).FirstOrDefault();
+ var entityType = principalProperty.DeclaringType as IReadOnlyEntityType;
+ var linkingRelationship = entityType?.FindRowInternalForeignKeys(storeObject).FirstOrDefault();
if (linkingRelationship == null)
{
break;
@@ -1728,7 +1759,7 @@ public static IEnumerable GetMappedStoreObjects(
this IReadOnlyProperty property,
StoreObjectType storeObjectType)
{
- var declaringType = property.DeclaringEntityType;
+ var declaringType = property.DeclaringType;
var declaringStoreObject = StoreObjectIdentifier.Create(declaringType, storeObjectType);
if (declaringStoreObject != null
&& property.GetColumnName(declaringStoreObject.Value) != null)
@@ -1754,13 +1785,16 @@ public static IEnumerable GetMappedStoreObjects(
yield break;
}
- foreach (var derivedType in declaringType.GetDerivedTypes())
+ if (declaringType is IReadOnlyEntityType entityType)
{
- var derivedStoreObject = StoreObjectIdentifier.Create(derivedType, storeObjectType);
- if (derivedStoreObject != null
- && property.GetColumnName(derivedStoreObject.Value) != null)
+ foreach (var derivedType in entityType.GetDerivedTypes())
{
- yield return derivedStoreObject.Value;
+ var derivedStoreObject = StoreObjectIdentifier.Create(derivedType, storeObjectType);
+ if (derivedStoreObject != null
+ && property.GetColumnName(derivedStoreObject.Value) != null)
+ {
+ yield return derivedStoreObject.Value;
+ }
}
}
}
@@ -1939,7 +1973,7 @@ private static TValue ThrowReadValueException(
///
public static string? GetJsonPropertyName(this IReadOnlyProperty property)
=> (string?)property.FindAnnotation(RelationalAnnotationNames.JsonPropertyName)?.Value
- ?? (property.IsKey() || !property.DeclaringEntityType.IsMappedToJson() ? null : property.Name);
+ ?? (property.IsKey() || !property.DeclaringType.IsMappedToJson() ? null : property.Name);
///
/// Sets the value of JSON property name used for the given property of an entity mapped to a JSON column.
diff --git a/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs b/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs
new file mode 100644
index 00000000000..43a037a3168
--- /dev/null
+++ b/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs
@@ -0,0 +1,372 @@
+// 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;
+
+///
+/// Type extension methods for relational database metadata.
+///
+///
+/// See Modeling entity types and relationships for more information and examples.
+///
+public static class RelationalTypeBaseExtensions
+{
+ #region Table mapping
+
+ ///
+ /// Returns the name of the table to which the type is mapped
+ /// or if not mapped to a table.
+ ///
+ /// The type to get the table name for.
+ /// The name of the table to which the type is mapped.
+ public static string? GetTableName(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetTableName();
+
+ ///
+ /// Returns the database schema that contains the mapped table.
+ ///
+ /// The type to get the schema for.
+ /// The database schema that contains the mapped table.
+ public static string? GetSchema(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetSchema();
+
+ ///
+ /// Returns the default mappings that the type would use.
+ ///
+ /// The type to get the table mappings for.
+ /// The tables to which the type is mapped.
+ public static IEnumerable GetDefaultMappings(this ITypeBase typeBase)
+ => (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.DefaultMappings)
+ ?? Enumerable.Empty();
+
+ ///
+ /// Returns the tables to which the type is mapped.
+ ///
+ /// The type to get the table mappings for.
+ /// The tables to which the type is mapped.
+ public static IEnumerable GetTableMappings(this ITypeBase typeBase)
+ => (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.TableMappings)
+ ?? Enumerable.Empty();
+
+ #endregion Table mapping
+
+ #region View mapping
+
+ ///
+ /// Returns the name of the view to which the type is mapped or if not mapped to a view.
+ ///
+ /// The type to get the view name for.
+ /// The name of the view to which the type is mapped.
+ public static string? GetViewName(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetViewName();
+
+ ///
+ /// Returns the database schema that contains the mapped view.
+ ///
+ /// The type to get the view schema for.
+ /// The database schema that contains the mapped view.
+ public static string? GetViewSchema(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetViewSchema();
+
+ ///
+ /// Returns the views to which the type is mapped.
+ ///
+ /// The type to get the view mappings for.
+ /// The views to which the type is mapped.
+ public static IEnumerable GetViewMappings(this ITypeBase typeBase)
+ => (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.ViewMappings)
+ ?? Enumerable.Empty();
+
+ #endregion View mapping
+
+ #region SQL query mapping
+
+ ///
+ /// Returns the SQL string used to provide data for the type or if not mapped to a SQL string.
+ ///
+ /// The type.
+ /// The SQL string used to provide data for the type.
+ public static string? GetSqlQuery(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetSqlQuery();
+
+ ///
+ /// Returns the SQL string mappings.
+ ///
+ /// The type to get the SQL string mappings for.
+ /// The SQL string to which the type is mapped.
+ public static IEnumerable GetSqlQueryMappings(this ITypeBase typeBase)
+ => (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.SqlQueryMappings)
+ ?? Enumerable.Empty();
+
+ #endregion SQL query mapping
+
+ #region Function mapping
+
+ ///
+ /// Returns the name of the function to which the type is mapped or if not mapped to a function.
+ ///
+ /// The type to get the function name for.
+ /// The name of the function to which the type is mapped.
+ public static string? GetFunctionName(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetFunctionName();
+
+ ///
+ /// Returns the functions to which the type is mapped.
+ ///
+ /// The type to get the function mappings for.
+ /// The functions to which the type is mapped.
+ public static IEnumerable GetFunctionMappings(this ITypeBase typeBase)
+ => (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.FunctionMappings)
+ ?? Enumerable.Empty();
+
+ #endregion
+
+ #region SProc mapping
+
+ ///
+ /// Returns the stored procedure to which the type is mapped for deletes
+ /// or if not mapped to a stored procedure.
+ ///
+ /// The type.
+ /// The stored procedure to which the type is mapped.
+ public static IReadOnlyStoredProcedure? GetDeleteStoredProcedure(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetDeleteStoredProcedure();
+
+ ///
+ /// Returns the stored procedure to which the type is mapped for deletes
+ /// or if not mapped to a stored procedure.
+ ///
+ /// The type.
+ /// The stored procedure to which the type is mapped.
+ public static IStoredProcedure? GetDeleteStoredProcedure(this ITypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetDeleteStoredProcedure();
+
+ ///
+ /// Returns the stored procedure to which the type is mapped for inserts
+ /// or if not mapped to a stored procedure.
+ ///
+ /// The type.
+ /// The stored procedure to which the type is mapped.
+ public static IReadOnlyStoredProcedure? GetInsertStoredProcedure(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetInsertStoredProcedure();
+
+ ///
+ /// Returns the stored procedure to which the type is mapped for inserts
+ /// or if not mapped to a stored procedure.
+ ///
+ /// The type.
+ /// The stored procedure to which the type is mapped.
+ public static IStoredProcedure? GetInsertStoredProcedure(this ITypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetInsertStoredProcedure();
+
+ ///
+ /// Returns the stored procedure to which the type is mapped for updates
+ /// or if not mapped to a stored procedure.
+ ///
+ /// The type.
+ /// The stored procedure to which the type is mapped.
+ public static IReadOnlyStoredProcedure? GetUpdateStoredProcedure(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetUpdateStoredProcedure();
+
+ ///
+ /// Returns the stored procedure to which the type is mapped for updates
+ /// or if not mapped to a stored procedure.
+ ///
+ /// The type.
+ /// The stored procedure to which the type is mapped.
+ public static IStoredProcedure? GetUpdateStoredProcedure(this ITypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetUpdateStoredProcedure();
+
+ ///
+ /// Returns the insert stored procedures to which the type is mapped.
+ ///
+ /// The type.
+ /// The insert stored procedures to which the type is mapped.
+ public static IEnumerable GetInsertStoredProcedureMappings(this ITypeBase typeBase)
+ => (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.InsertStoredProcedureMappings)
+ ?? Enumerable.Empty();
+
+ ///
+ /// Returns the delete stored procedures to which the type is mapped.
+ ///
+ /// The type.
+ /// The delete stored procedures to which the type is mapped.
+ public static IEnumerable GetDeleteStoredProcedureMappings(this ITypeBase typeBase)
+ => (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.DeleteStoredProcedureMappings)
+ ?? Enumerable.Empty();
+
+ ///
+ /// Returns the update stored procedures to which the type is mapped.
+ ///
+ /// The type.
+ /// The update stored procedures to which the type is mapped.
+ public static IEnumerable GetUpdateStoredProcedureMappings(this ITypeBase typeBase)
+ => (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.UpdateStoredProcedureMappings)
+ ?? Enumerable.Empty();
+
+ #endregion
+
+ #region Mapping Fragments
+
+ ///
+ ///
+ /// Returns all configured type mapping fragments.
+ ///
+ ///
+ /// This method is typically used by database providers (and other extensions). It is generally
+ /// not used in application code.
+ ///
+ ///
+ /// The type.
+ /// The configured type mapping fragments.
+ public static IEnumerable GetMappingFragments(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetMappingFragments();
+
+ ///
+ ///
+ /// Returns all configured type mapping fragments.
+ ///
+ ///
+ /// This method is typically used by database providers (and other extensions). It is generally
+ /// not used in application code.
+ ///
+ ///
+ /// The type.
+ /// The configured type mapping fragments.
+ public static IEnumerable GetMappingFragments(this ITypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetMappingFragments();
+
+ ///
+ ///
+ /// Returns all configured type mapping fragments of the given type.
+ ///
+ ///
+ /// This method is typically used by database providers (and other extensions). It is generally
+ /// not used in application code.
+ ///
+ ///
+ /// The type.
+ /// The type of store object to get the mapping fragments for.
+ /// The configured type mapping fragments.
+ public static IEnumerable GetMappingFragments(
+ this IReadOnlyTypeBase typeBase,
+ StoreObjectType storeObjectType)
+ => typeBase.FundamentalEntityType.GetMappingFragments(storeObjectType);
+
+ ///
+ ///
+ /// Returns all configured type mapping fragments of the given type.
+ ///
+ ///
+ /// This method is typically used by database providers (and other extensions). It is generally
+ /// not used in application code.
+ ///
+ ///
+ /// The type.
+ /// The type of store object to get the mapping fragments for.
+ /// The configured type mapping fragments.
+ public static IEnumerable GetMappingFragments(
+ this ITypeBase typeBase,
+ StoreObjectType storeObjectType)
+ => typeBase.FundamentalEntityType.GetMappingFragments(storeObjectType);
+
+ ///
+ ///
+ /// Returns the type mapping for a particular table-like store object.
+ ///
+ ///
+ /// This method is typically used by database providers (and other extensions). It is generally
+ /// not used in application code.
+ ///
+ ///
+ /// The type.
+ /// The identifier of a table-like store object.
+ /// An object that represents an type mapping fragment.
+ public static IReadOnlyEntityTypeMappingFragment? FindMappingFragment(
+ this IReadOnlyTypeBase typeBase,
+ in StoreObjectIdentifier storeObject)
+ => typeBase.FundamentalEntityType.FindMappingFragment(storeObject);
+
+ ///
+ ///
+ /// Returns the type mapping for a particular table-like store object.
+ ///
+ ///
+ /// This method is typically used by database providers (and other extensions). It is generally
+ /// not used in application code.
+ ///
+ ///
+ /// The type.
+ /// The identifier of a table-like store object.
+ /// An object that represents an type mapping fragment.
+ public static IEntityTypeMappingFragment? FindMappingFragment(
+ this ITypeBase typeBase,
+ in StoreObjectIdentifier storeObject)
+ => typeBase.FundamentalEntityType.FindMappingFragment(storeObject);
+
+ #endregion
+
+ #region Mapping strategy
+
+ ///
+ /// Gets the mapping strategy for the derived types.
+ ///
+ /// The type.
+ /// The mapping strategy for the derived types.
+ public static string? GetMappingStrategy(this IReadOnlyTypeBase typeBase)
+ => typeBase.FundamentalEntityType.GetMappingStrategy();
+
+ #endregion Mapping strategy
+
+ #region Json
+
+ ///
+ /// Gets a value indicating whether the specified entity is mapped to a JSON column.
+ ///
+ /// The type.
+ /// A value indicating whether the associated type is mapped to a JSON column.
+ public static bool IsMappedToJson(this IReadOnlyTypeBase typeBase)
+ => !string.IsNullOrEmpty(typeBase.GetContainerColumnName());
+
+ ///
+ /// Gets the container column name to which the type is mapped.
+ ///
+ /// The type to get the container column name for.
+ /// The container column name to which the type is mapped.
+ public static string? GetContainerColumnName(this IReadOnlyTypeBase typeBase)
+ => typeBase.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string columnName
+ ? columnName
+ : typeBase is IReadOnlyEntityType entityType
+ ? (entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnName())
+ : ((IReadOnlyComplexType)typeBase).ComplexProperty.DeclaringType.GetContainerColumnName();
+
+ ///
+ /// Gets the value of JSON property name used for the given entity mapped to a JSON column.
+ ///
+ ///
+ /// Unless configured explicitly, navigation name is used.
+ ///
+ /// The type.
+ ///
+ /// The value for the JSON property used to store this type.
+ /// is returned for entities that are not mapped to a JSON column.
+ ///
+ public static string? GetJsonPropertyName(this IReadOnlyTypeBase typeBase)
+ => (string?)typeBase.FindAnnotation(RelationalAnnotationNames.JsonPropertyName)?.Value
+ ?? (!typeBase.IsMappedToJson()
+ ? null
+ : typeBase is IReadOnlyEntityType entityType
+ ? entityType.FindOwnership()!.GetNavigation(pointsToPrincipal: false)!.Name
+ : ((IReadOnlyComplexType)typeBase).ComplexProperty.Name);
+
+ #endregion
+}
diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
index a65590104d6..a364761733e 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
@@ -680,7 +680,7 @@ protected virtual void ValidateBoolsWithDefaults(
&& property.ValueGenerated != ValueGenerated.Never
&& property.FieldInfo?.FieldType.IsNullableType() != true
&& !((IConventionProperty)property).GetSentinelConfigurationSource().HasValue
- && (StoreObjectIdentifier.Create(property.DeclaringEntityType, StoreObjectType.Table) is { } table
+ && (StoreObjectIdentifier.Create(property.DeclaringType, StoreObjectType.Table) is { } table
&& (IsNotNullAndNotDefault(property.GetDefaultValue(table))
|| property.GetDefaultValueSql(table) != null)))
{
@@ -1231,14 +1231,14 @@ protected virtual void ValidateSharedColumnsCompatibility(
continue;
}
- if (property.DeclaringEntityType.IsAssignableFrom(duplicateProperty.DeclaringEntityType)
- || duplicateProperty.DeclaringEntityType.IsAssignableFrom(property.DeclaringEntityType))
+ if (property.DeclaringType.IsAssignableFrom(duplicateProperty.DeclaringType)
+ || duplicateProperty.DeclaringType.IsAssignableFrom(property.DeclaringType))
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameSameHierarchy(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
@@ -1298,9 +1298,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameNullabilityMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
@@ -1312,9 +1312,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameMaxLengthMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1326,9 +1326,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameUnicodenessMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
@@ -1338,9 +1338,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameFixedLengthMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
@@ -1352,9 +1352,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNamePrecisionMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1368,9 +1368,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameScaleMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1382,9 +1382,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameConcurrencyTokenMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
@@ -1396,9 +1396,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameDataTypeMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1422,9 +1422,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameProviderTypeMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1438,9 +1438,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameComputedSqlMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1454,9 +1454,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameIsStoredMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1477,9 +1477,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameDefaultSqlMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1494,9 +1494,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameDefaultSqlMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1510,9 +1510,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameCommentMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1526,9 +1526,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameCollationMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -1542,9 +1542,9 @@ protected virtual void ValidateCompatible(
{
throw new InvalidOperationException(
RelationalStrings.DuplicateColumnNameOrderMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName(),
@@ -2335,10 +2335,10 @@ private static IEnumerable GetAllMappedStoreObjects(
IReadOnlyProperty property,
StoreObjectType storeObjectType)
{
- var mappingStrategy = property.DeclaringEntityType.GetMappingStrategy();
+ var mappingStrategy = property.DeclaringType.GetMappingStrategy();
if (property.IsPrimaryKey())
{
- var declaringStoreObject = StoreObjectIdentifier.Create(property.DeclaringEntityType, storeObjectType);
+ var declaringStoreObject = StoreObjectIdentifier.Create(property.DeclaringType, storeObjectType);
if (declaringStoreObject != null)
{
yield return declaringStoreObject.Value;
@@ -2349,28 +2349,32 @@ private static IEnumerable GetAllMappedStoreObjects(
yield break;
}
- foreach (var fragment in property.DeclaringEntityType.GetMappingFragments(storeObjectType))
+ foreach (var fragment in property.DeclaringType.GetMappingFragments(storeObjectType))
{
yield return fragment.StoreObject;
}
- foreach (var containingType in property.DeclaringEntityType.GetDerivedTypes())
+ if (property.DeclaringType is IReadOnlyEntityType entityType)
{
- var storeObject = StoreObjectIdentifier.Create(containingType, storeObjectType);
- if (storeObject != null)
+ foreach (var containingType in entityType.GetDerivedTypes())
{
- yield return storeObject.Value;
-
- if (mappingStrategy == RelationalAnnotationNames.TphMappingStrategy)
+ var storeObject = StoreObjectIdentifier.Create(containingType, storeObjectType);
+ if (storeObject != null)
{
- yield break;
+ yield return storeObject.Value;
+
+ if (mappingStrategy == RelationalAnnotationNames.TphMappingStrategy)
+ {
+ yield break;
+ }
}
}
+ yield break;
}
}
else
{
- var declaringStoreObject = StoreObjectIdentifier.Create(property.DeclaringEntityType, storeObjectType);
+ var declaringStoreObject = StoreObjectIdentifier.Create(property.DeclaringType, storeObjectType);
if (storeObjectType is StoreObjectType.Function or StoreObjectType.SqlQuery)
{
if (declaringStoreObject != null)
@@ -2383,7 +2387,7 @@ private static IEnumerable GetAllMappedStoreObjects(
if (declaringStoreObject != null)
{
- var fragments = property.DeclaringEntityType.GetMappingFragments(storeObjectType).ToList();
+ var fragments = property.DeclaringType.GetMappingFragments(storeObjectType).ToList();
if (fragments.Count > 0)
{
var overrides = RelationalPropertyOverrides.Find(property, declaringStoreObject.Value);
@@ -2411,9 +2415,14 @@ private static IEnumerable GetAllMappedStoreObjects(
}
}
+ if (property.DeclaringType is not IReadOnlyEntityType entityType)
+ {
+ yield break;
+ }
+
var tableFound = false;
var queue = new Queue();
- queue.Enqueue(property.DeclaringEntityType);
+ queue.Enqueue(entityType);
while (queue.Count > 0 && !tableFound)
{
foreach (var containingType in queue.Dequeue().GetDirectlyDerivedTypes())
diff --git a/src/EFCore.Relational/Infrastructure/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Infrastructure/RelationalPropertyExtensions.cs
index 7b0eeb40cbd..07ed92a0245 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalPropertyExtensions.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalPropertyExtensions.cs
@@ -4,7 +4,7 @@
namespace Microsoft.EntityFrameworkCore.Infrastructure;
///
-/// Relational extension methods for .
+/// Relational extension methods for .
///
public static class RelationalPropertyExtensions
{
diff --git a/src/EFCore.Relational/Metadata/Builders/IConventionCheckConstraintBuilder.cs b/src/EFCore.Relational/Metadata/Builders/IConventionCheckConstraintBuilder.cs
index a1240ea6ddc..24ff53947f3 100644
--- a/src/EFCore.Relational/Metadata/Builders/IConventionCheckConstraintBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/IConventionCheckConstraintBuilder.cs
@@ -17,6 +17,45 @@ public interface IConventionCheckConstraintBuilder : IConventionAnnotatableBuild
///
new IConventionCheckConstraint Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionCheckConstraintBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionCheckConstraintBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionCheckConstraintBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Sets the database name of the check constraint.
///
diff --git a/src/EFCore.Relational/Metadata/Builders/IConventionDbFunctionBuilder.cs b/src/EFCore.Relational/Metadata/Builders/IConventionDbFunctionBuilder.cs
index dd7f26dd28b..57a68e49323 100644
--- a/src/EFCore.Relational/Metadata/Builders/IConventionDbFunctionBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/IConventionDbFunctionBuilder.cs
@@ -18,6 +18,45 @@ public interface IConventionDbFunctionBuilder : IConventionAnnotatableBuilder
///
new IConventionDbFunction Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionDbFunctionBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionDbFunctionBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionDbFunctionBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Sets the name of the database function.
///
diff --git a/src/EFCore.Relational/Metadata/Builders/IConventionDbFunctionParameterBuilder.cs b/src/EFCore.Relational/Metadata/Builders/IConventionDbFunctionParameterBuilder.cs
index 0f12ad0fa8c..0b4a924be72 100644
--- a/src/EFCore.Relational/Metadata/Builders/IConventionDbFunctionParameterBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/IConventionDbFunctionParameterBuilder.cs
@@ -16,6 +16,45 @@ public interface IConventionDbFunctionParameterBuilder : IConventionAnnotatableB
///
new IConventionDbFunctionParameter Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionDbFunctionParameterBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionDbFunctionParameterBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionDbFunctionParameterBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Sets the store type of the function parameter in the database.
///
diff --git a/src/EFCore.Relational/Metadata/Builders/IConventionSequenceBuilder.cs b/src/EFCore.Relational/Metadata/Builders/IConventionSequenceBuilder.cs
index 2cfb880b98c..4a96d08ac73 100644
--- a/src/EFCore.Relational/Metadata/Builders/IConventionSequenceBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/IConventionSequenceBuilder.cs
@@ -16,6 +16,45 @@ public interface IConventionSequenceBuilder : IConventionAnnotatableBuilder
///
new IConventionSequence Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionSequenceBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionSequenceBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionSequenceBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Sets the type of values returned by the sequence.
///
diff --git a/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureBuilder.cs b/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureBuilder.cs
index 5e33363857a..9b6b114d65b 100644
--- a/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureBuilder.cs
@@ -16,6 +16,45 @@ public interface IConventionStoredProcedureBuilder : IConventionAnnotatableBuild
///
new IConventionStoredProcedure Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionStoredProcedureBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionStoredProcedureBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionStoredProcedureBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Sets the name of the stored procedure.
///
diff --git a/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureParameterBuilder.cs b/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureParameterBuilder.cs
index 3daf64fe4c8..671ec1ddf15 100644
--- a/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureParameterBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureParameterBuilder.cs
@@ -18,6 +18,45 @@ public interface IConventionStoredProcedureParameterBuilder : IConventionAnnotat
///
new IConventionStoredProcedureParameter Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionStoredProcedureParameterBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionStoredProcedureParameterBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionStoredProcedureParameterBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Configures the parameter name.
///
diff --git a/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureResultColumnBuilder.cs b/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureResultColumnBuilder.cs
index b8723de8784..8fe535803e3 100644
--- a/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureResultColumnBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/IConventionStoredProcedureResultColumnBuilder.cs
@@ -16,6 +16,45 @@ public interface IConventionStoredProcedureResultColumnBuilder : IConventionAnno
///
new IConventionStoredProcedureResultColumn Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionStoredProcedureResultColumnBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionStoredProcedureResultColumnBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionStoredProcedureResultColumnBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Configures the result column name.
///
diff --git a/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder.cs b/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder.cs
index e7a7a204c49..59be88ce498 100644
--- a/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/StoredProcedureBuilder.cs
@@ -226,7 +226,7 @@ protected virtual PropertyBuilder CreatePropertyBuilder(string propertyName)
#pragma warning disable EF1001 // Internal EF Core API usage.
return new ModelBuilder(entityType.Model)
#pragma warning restore EF1001 // Internal EF Core API usage.
- .Entity(property.DeclaringEntityType.Name)
+ .Entity(property.DeclaringType.Name)
.Property(property.ClrType, propertyName);
}
diff --git a/src/EFCore.Relational/Metadata/Conventions/PropertyOverridesConvention.cs b/src/EFCore.Relational/Metadata/Conventions/PropertyOverridesConvention.cs
index 5a9c7479b3e..a45fc972989 100644
--- a/src/EFCore.Relational/Metadata/Conventions/PropertyOverridesConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/PropertyOverridesConvention.cs
@@ -43,7 +43,7 @@ public virtual void ProcessPropertyAdded(
IConventionContext context)
{
var property = propertyBuilder.Metadata;
- if (!property.DeclaringEntityType.HasSharedClrType)
+ if (!property.DeclaringType.HasSharedClrType)
{
return;
}
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalColumnAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalColumnAttributeConvention.cs
index cdfed28f535..8669ae16b76 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalColumnAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalColumnAttributeConvention.cs
@@ -31,13 +31,7 @@ public RelationalColumnAttributeConvention(
///
protected virtual RelationalConventionSetBuilderDependencies RelationalDependencies { get; }
- ///
- /// Called after a property is added to the entity type with an attribute on the associated CLR property or field.
- ///
- /// The builder for the property.
- /// The attribute.
- /// The member that has the attribute.
- /// Additional information associated with convention execution.
+ ///
protected override void ProcessPropertyAdded(
IConventionPropertyBuilder propertyBuilder,
ColumnAttribute attribute,
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalColumnCommentAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalColumnCommentAttributeConvention.cs
index daebc058db8..4b4277cdc19 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalColumnCommentAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalColumnCommentAttributeConvention.cs
@@ -29,13 +29,7 @@ public RelationalColumnCommentAttributeConvention(
///
protected virtual RelationalConventionSetBuilderDependencies RelationalDependencies { get; }
- ///
- /// Called after a property is added to the entity type with an attribute on the associated CLR property or field.
- ///
- /// The builder for the property.
- /// The attribute.
- /// The member that has the attribute.
- /// Additional information associated with convention execution.
+ ///
protected override void ProcessPropertyAdded(
IConventionPropertyBuilder propertyBuilder,
CommentAttribute attribute,
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalTableAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalTableAttributeConvention.cs
index 9b154b2ded5..3226db5a8dd 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalTableAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalTableAttributeConvention.cs
@@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
/// See Model building conventions for more information and examples.
///
-public class RelationalTableAttributeConvention : EntityTypeAttributeConventionBase
+public class RelationalTableAttributeConvention : TypeAttributeConventionBase
{
///
/// Creates a new instance of .
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalTableCommentAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalTableCommentAttributeConvention.cs
index 45dd1764914..ba202eb015e 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalTableCommentAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalTableCommentAttributeConvention.cs
@@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
/// See Model building conventions for more information and examples.
///
-public class RelationalTableCommentAttributeConvention : EntityTypeAttributeConventionBase
+public class RelationalTableCommentAttributeConvention : TypeAttributeConventionBase
{
///
/// Creates a new instance of .
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs
index b480b83dab7..d0bd5b633cf 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs
@@ -37,14 +37,7 @@ public RelationalValueGenerationConvention(
///
protected virtual RelationalConventionSetBuilderDependencies RelationalDependencies { get; }
- ///
- /// Called after an annotation is changed on a property.
- ///
- /// The builder for the property.
- /// The annotation name.
- /// The new annotation.
- /// The old annotation.
- /// Additional information associated with convention execution.
+ ///
public virtual void ProcessPropertyAnnotationChanged(
IConventionPropertyBuilder propertyBuilder,
string name,
@@ -193,13 +186,13 @@ private void ProcessTableChanged(
protected override ValueGenerated? GetValueGenerated(IConventionProperty property)
{
var table = property.GetMappedStoreObjects(StoreObjectType.Table).FirstOrDefault();
- return !MappingStrategyAllowsValueGeneration(property, property.DeclaringEntityType.GetMappingStrategy())
+ return !MappingStrategyAllowsValueGeneration(property, property.DeclaringType.GetMappingStrategy())
? null
: table.Name != null
? GetValueGenerated(property, table)
- : property.DeclaringEntityType.IsMappedToJson()
- && !property.DeclaringEntityType.FindOwnership()!.IsUnique
+ : property.DeclaringType.IsMappedToJson()
&& property.IsOrdinalKeyProperty()
+ && (property.DeclaringType as IReadOnlyEntityType)?.FindOwnership()!.IsUnique == false
? ValueGenerated.OnAddOrUpdate
: property.GetMappedStoreObjects(StoreObjectType.InsertStoredProcedure).Any()
? GetValueGenerated((IReadOnlyProperty)property)
diff --git a/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs b/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs
index 7c8475c9401..5692e85e53d 100644
--- a/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs
@@ -250,11 +250,11 @@ private static void TryUniquifyColumnNames(
continue;
}
- var usePrefix = property.DeclaringEntityType != otherProperty.DeclaringEntityType;
+ var usePrefix = property.DeclaringType != otherProperty.DeclaringType;
if (!usePrefix
- || (!property.DeclaringEntityType.IsStrictlyDerivedFrom(otherProperty.DeclaringEntityType)
- && !otherProperty.DeclaringEntityType.IsStrictlyDerivedFrom(property.DeclaringEntityType))
- || property.DeclaringEntityType.FindRowInternalForeignKeys(storeObject).Any())
+ || (!property.DeclaringType.IsStrictlyDerivedFrom(otherProperty.DeclaringType)
+ && !otherProperty.DeclaringType.IsStrictlyDerivedFrom(property.DeclaringType))
+ || (property.DeclaringType as IConventionEntityType)?.FindRowInternalForeignKeys(storeObject).Any() == true)
{
var newColumnName = TryUniquify(property, columnName, properties, storeObject, usePrefix, maxLength);
if (newColumnName != null)
@@ -265,9 +265,9 @@ private static void TryUniquifyColumnNames(
}
if (!usePrefix
- || (!property.DeclaringEntityType.IsStrictlyDerivedFrom(otherProperty.DeclaringEntityType)
- && !otherProperty.DeclaringEntityType.IsStrictlyDerivedFrom(property.DeclaringEntityType))
- || otherProperty.DeclaringEntityType.FindRowInternalForeignKeys(storeObject).Any())
+ || (!property.DeclaringType.IsStrictlyDerivedFrom(otherProperty.DeclaringType)
+ && !otherProperty.DeclaringType.IsStrictlyDerivedFrom(property.DeclaringType))
+ || (otherProperty.DeclaringType as IConventionEntityType)?.FindRowInternalForeignKeys(storeObject).Any() == true)
{
var newOtherColumnName = TryUniquify(otherProperty, columnName, properties, storeObject, usePrefix, maxLength);
if (newOtherColumnName != null)
@@ -292,7 +292,7 @@ private static void TryUniquifyColumnNames(
{
if (usePrefix)
{
- var prefix = property.DeclaringEntityType.ShortName();
+ var prefix = property.DeclaringType.ShortName();
if (!columnName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
columnName = prefix + "_" + columnName;
diff --git a/src/EFCore.Relational/Metadata/Conventions/TableSharingConcurrencyTokenConvention.cs b/src/EFCore.Relational/Metadata/Conventions/TableSharingConcurrencyTokenConvention.cs
index 6d09e1af470..9dd98b18a63 100644
--- a/src/EFCore.Relational/Metadata/Conventions/TableSharingConcurrencyTokenConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/TableSharingConcurrencyTokenConvention.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
@@ -216,22 +218,24 @@ public static bool IsConcurrencyTokenMissing(
var propertyMissing = true;
foreach (var mappedProperty in propertiesMappedToConcurrencyColumn)
{
- var declaringEntityType = mappedProperty.DeclaringEntityType;
- if (declaringEntityType.IsAssignableFrom(entityType)
- || entityType.IsAssignableFrom(declaringEntityType)
- || declaringEntityType.IsInOwnershipPath(entityType)
- || entityType.IsInOwnershipPath(declaringEntityType))
+ var declaringType = mappedProperty.DeclaringType;
+ var declaringEntityType = declaringType as IEntityType;
+ if (declaringType.IsAssignableFrom(entityType)
+ || entityType.IsAssignableFrom(declaringType)
+ || declaringEntityType != null
+ && (declaringEntityType.IsInOwnershipPath(entityType)
+ || entityType.IsInOwnershipPath(declaringEntityType)))
{
// The concurrency token is on the base type, derived type or in the same aggregate
propertyMissing = false;
continue;
}
- var linkingFks = declaringEntityType.FindForeignKeys(declaringEntityType.FindPrimaryKey()!.Properties)
- .Where(
- fk => fk.PrincipalKey.IsPrimaryKey()
+ var linkingFks = declaringEntityType?.FindForeignKeys(declaringEntityType.FindPrimaryKey()!.Properties)
+ .Where(fk => fk.PrincipalKey.IsPrimaryKey()
&& mappedTypes.Contains(fk.PrincipalEntityType)).ToList();
- if (linkingFks.Count > 0
+ if (linkingFks != null
+ && linkingFks.Count > 0
&& linkingFks.All(fk => fk.PrincipalEntityType != entityType)
&& linkingFks.Any(
fk => fk.PrincipalEntityType.IsAssignableFrom(entityType)
@@ -266,7 +270,7 @@ private static void RemoveDerivedEntityTypes(
}
if (!removed
- && entityType.IsAssignableFrom(property.DeclaringEntityType))
+ && entityType.IsAssignableFrom(property.DeclaringType))
{
entityTypeDictionary.Remove(entityType);
}
diff --git a/src/EFCore.Relational/Metadata/IColumnBase.cs b/src/EFCore.Relational/Metadata/IColumnBase.cs
index 238099e2c73..e82ac0bd7a7 100644
--- a/src/EFCore.Relational/Metadata/IColumnBase.cs
+++ b/src/EFCore.Relational/Metadata/IColumnBase.cs
@@ -65,7 +65,7 @@ ValueComparer ProviderValueComparer
for (var i = 0; i < PropertyMappings.Count; i++)
{
var mapping = PropertyMappings[i];
- if (mapping.Property.DeclaringEntityType.IsAssignableFrom(entityType))
+ if (mapping.Property.DeclaringType.IsAssignableFrom(entityType))
{
return mapping;
}
diff --git a/src/EFCore.Relational/Metadata/IColumnMapping.cs b/src/EFCore.Relational/Metadata/IColumnMapping.cs
index a7f4796de37..49a9f07d9e8 100644
--- a/src/EFCore.Relational/Metadata/IColumnMapping.cs
+++ b/src/EFCore.Relational/Metadata/IColumnMapping.cs
@@ -49,7 +49,7 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt
}
builder
- .Append(Property.DeclaringEntityType.DisplayName())
+ .Append(Property.DeclaringType.DisplayName())
.Append('.')
.Append(Property.Name)
.Append(" - ");
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalCheckConstraintBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalCheckConstraintBuilder.cs
index 5ef202fa24f..cddb8f80c31 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalCheckConstraintBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalCheckConstraintBuilder.cs
@@ -174,6 +174,24 @@ IConventionCheckConstraint IConventionCheckConstraintBuilder.Metadata
get => Metadata;
}
+ ///
+ [DebuggerStepThrough]
+ IConventionCheckConstraintBuilder? IConventionCheckConstraintBuilder.HasAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionCheckConstraintBuilder?)base.HasAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionCheckConstraintBuilder? IConventionCheckConstraintBuilder.HasNonNullAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionCheckConstraintBuilder?)base.HasNonNullAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionCheckConstraintBuilder? IConventionCheckConstraintBuilder.HasNoAnnotation(string name, bool fromDataAnnotation)
+ => (IConventionCheckConstraintBuilder?)base.HasNoAnnotation(
+ name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
[DebuggerStepThrough]
IConventionCheckConstraintBuilder? IConventionCheckConstraintBuilder.HasName(string? name, bool fromDataAnnotation)
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionBuilder.cs
index ea8b52edbc7..5ccc41eb578 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionBuilder.cs
@@ -246,6 +246,39 @@ IConventionDbFunction IConventionDbFunctionBuilder.Metadata
get => Metadata;
}
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
+ IConventionDbFunctionBuilder? IConventionDbFunctionBuilder.HasAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionDbFunctionBuilder?)base.HasAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
+ IConventionDbFunctionBuilder? IConventionDbFunctionBuilder.HasNonNullAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionDbFunctionBuilder?)base.HasNonNullAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// 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.
+ ///
+ [DebuggerStepThrough]
+ IConventionDbFunctionBuilder? IConventionDbFunctionBuilder.HasNoAnnotation(string name, bool fromDataAnnotation)
+ => (IConventionDbFunctionBuilder?)base.HasNoAnnotation(
+ name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
[DebuggerStepThrough]
IConventionDbFunctionBuilder? IConventionDbFunctionBuilder.HasName(string? name, bool fromDataAnnotation)
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionParameterBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionParameterBuilder.cs
index f455fc063c3..3d1bacc7501 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionParameterBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionParameterBuilder.cs
@@ -127,6 +127,24 @@ IConventionDbFunctionParameter IConventionDbFunctionParameterBuilder.Metadata
get => Metadata;
}
+ ///
+ [DebuggerStepThrough]
+ IConventionDbFunctionParameterBuilder? IConventionDbFunctionParameterBuilder.HasAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionDbFunctionParameterBuilder?)base.HasAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionDbFunctionParameterBuilder? IConventionDbFunctionParameterBuilder.HasNonNullAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionDbFunctionParameterBuilder?)base.HasNonNullAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionDbFunctionParameterBuilder? IConventionDbFunctionParameterBuilder.HasNoAnnotation(string name, bool fromDataAnnotation)
+ => (IConventionDbFunctionParameterBuilder?)base.HasNoAnnotation(
+ name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
[DebuggerStepThrough]
IConventionDbFunctionParameterBuilder? IConventionDbFunctionParameterBuilder.HasStoreType(
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalSequenceBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalSequenceBuilder.cs
index 9b3a1b6372a..1424a93c02e 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalSequenceBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalSequenceBuilder.cs
@@ -195,6 +195,24 @@ IConventionSequence IConventionSequenceBuilder.Metadata
get => Metadata;
}
+ ///
+ [DebuggerStepThrough]
+ IConventionSequenceBuilder? IConventionSequenceBuilder.HasAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionSequenceBuilder?)base.HasAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionSequenceBuilder? IConventionSequenceBuilder.HasNonNullAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionSequenceBuilder?)base.HasNonNullAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionSequenceBuilder? IConventionSequenceBuilder.HasNoAnnotation(string name, bool fromDataAnnotation)
+ => (IConventionSequenceBuilder?)base.HasNoAnnotation(
+ name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
[DebuggerStepThrough]
IConventionSequenceBuilder? IConventionSequenceBuilder.HasType(Type? type, bool fromDataAnnotation)
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureBuilder.cs
index f437eab0f70..df64a52ece3 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureBuilder.cs
@@ -420,6 +420,24 @@ IConventionStoredProcedure IConventionStoredProcedureBuilder.Metadata
get => Metadata;
}
+ ///
+ [DebuggerStepThrough]
+ IConventionStoredProcedureBuilder? IConventionStoredProcedureBuilder.HasAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionStoredProcedureBuilder?)base.HasAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionStoredProcedureBuilder? IConventionStoredProcedureBuilder.HasNonNullAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionStoredProcedureBuilder?)base.HasNonNullAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionStoredProcedureBuilder? IConventionStoredProcedureBuilder.HasNoAnnotation(string name, bool fromDataAnnotation)
+ => (IConventionStoredProcedureBuilder?)base.HasNoAnnotation(
+ name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
[DebuggerStepThrough]
IConventionStoredProcedureBuilder? IConventionStoredProcedureBuilder.HasName(string? name, bool fromDataAnnotation)
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureParameterBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureParameterBuilder.cs
index d9ac813a5cf..69579f077bf 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureParameterBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureParameterBuilder.cs
@@ -100,6 +100,24 @@ IConventionStoredProcedureParameter IConventionStoredProcedureParameterBuilder.M
get => Metadata;
}
+ ///
+ [DebuggerStepThrough]
+ IConventionStoredProcedureParameterBuilder? IConventionStoredProcedureParameterBuilder.HasAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionStoredProcedureParameterBuilder?)base.HasAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionStoredProcedureParameterBuilder? IConventionStoredProcedureParameterBuilder.HasNonNullAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionStoredProcedureParameterBuilder?)base.HasNonNullAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionStoredProcedureParameterBuilder? IConventionStoredProcedureParameterBuilder.HasNoAnnotation(string name, bool fromDataAnnotation)
+ => (IConventionStoredProcedureParameterBuilder?)base.HasNoAnnotation(
+ name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
[DebuggerStepThrough]
IConventionStoredProcedureParameterBuilder? IConventionStoredProcedureParameterBuilder.HasName(string name, bool fromDataAnnotation)
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureResultColumnBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureResultColumnBuilder.cs
index a42e64c867e..82bcd8afc3f 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureResultColumnBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalStoredProcedureResultColumnBuilder.cs
@@ -65,6 +65,24 @@ IConventionStoredProcedureResultColumn IConventionStoredProcedureResultColumnBui
get => Metadata;
}
+ ///
+ [DebuggerStepThrough]
+ IConventionStoredProcedureResultColumnBuilder? IConventionStoredProcedureResultColumnBuilder.HasAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionStoredProcedureResultColumnBuilder?)base.HasAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionStoredProcedureResultColumnBuilder? IConventionStoredProcedureResultColumnBuilder.HasNonNullAnnotation(string name, object? value, bool fromDataAnnotation)
+ => (IConventionStoredProcedureResultColumnBuilder?)base.HasNonNullAnnotation(
+ name, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ IConventionStoredProcedureResultColumnBuilder? IConventionStoredProcedureResultColumnBuilder.HasNoAnnotation(string name, bool fromDataAnnotation)
+ => (IConventionStoredProcedureResultColumnBuilder?)base.HasNoAnnotation(
+ name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
[DebuggerStepThrough]
IConventionStoredProcedureResultColumnBuilder? IConventionStoredProcedureResultColumnBuilder.HasName(
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
index da1b1879f4c..a4fee3746cc 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
@@ -342,7 +342,7 @@ private static void AddDefaultMappings(
{
foreach (var property in entityType.GetProperties())
{
- var columnName = property.IsPrimaryKey() || isTpc || isTph || property.DeclaringEntityType == mappedType
+ var columnName = property.IsPrimaryKey() || isTpc || isTph || property.DeclaringType == mappedType
? property.GetColumnName()
: null;
if (columnName == null)
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs
index 68cd26ccb04..c0cfebbde1b 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs
@@ -20,4 +20,16 @@ public static class RelationalPropertyExtensions
[DebuggerStepThrough]
public static string? GetConfiguredColumnType(this IReadOnlyProperty property)
=> (string?)property[RelationalAnnotationNames.ColumnType];
+
+ ///
+ /// 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.
+ ///
+ public static bool IsOrdinalKeyProperty(this IReadOnlyProperty property)
+ => property.FindContainingPrimaryKey() is IReadOnlyKey { Properties.Count: > 1 }
+ && !property.IsForeignKey()
+ && property.ClrType == typeof(int)
+ && property.GetJsonPropertyName() == null;
}
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
index 45b56c7cf8f..9dde0563fdf 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
@@ -36,7 +36,7 @@ public RelationalPropertyOverrides(
StoreObject = storeObject;
_configurationSource = configurationSource;
_builder = new InternalRelationalPropertyOverridesBuilder(
- this, ((IConventionModel)property.DeclaringEntityType.Model).Builder);
+ this, ((IConventionModel)property.DeclaringType.Model).Builder);
}
///
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs
index 00f38a9abab..24a701fd9f6 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs
@@ -152,7 +152,7 @@ public virtual string Name
var baseName = GetProperty().GetDefaultColumnName(
((IReadOnlyStoredProcedure)StoredProcedure).GetStoreIdentifier()!.Value)!;
return ForOriginalValue ?? false
- ? Uniquifier.Truncate(baseName + "_Original", GetProperty().DeclaringEntityType.Model.GetMaxIdentifierLength())
+ ? Uniquifier.Truncate(baseName + "_Original", GetProperty().DeclaringType.Model.GetMaxIdentifierLength())
: baseName;
}
set => SetName(value, ConfigurationSource.Explicit);
diff --git a/src/EFCore.Relational/Metadata/StoreObjectIdentifier.cs b/src/EFCore.Relational/Metadata/StoreObjectIdentifier.cs
index 070aa49b81c..b58223a541b 100644
--- a/src/EFCore.Relational/Metadata/StoreObjectIdentifier.cs
+++ b/src/EFCore.Relational/Metadata/StoreObjectIdentifier.cs
@@ -21,39 +21,39 @@ private StoreObjectIdentifier(StoreObjectType storeObjectType, string name, stri
///
/// Creates an id for the store object that the given entity type is mapped to.
///
- /// The entity type.
+ /// The entity type.
/// The store object type.
/// The store object id.
- public static StoreObjectIdentifier? Create(IReadOnlyEntityType entityType, StoreObjectType type)
+ public static StoreObjectIdentifier? Create(IReadOnlyTypeBase typeBase, StoreObjectType type)
{
- Check.NotNull(entityType, nameof(entityType));
+ Check.NotNull(typeBase, nameof(typeBase));
switch (type)
{
case StoreObjectType.Table:
- var tableName = entityType.GetTableName();
- return tableName == null ? null : Table(tableName, entityType.GetSchema());
+ var tableName = typeBase.GetTableName();
+ return tableName == null ? null : Table(tableName, typeBase.GetSchema());
case StoreObjectType.View:
- var viewName = entityType.GetViewName();
- return viewName == null ? null : View(viewName, entityType.GetViewSchema());
+ var viewName = typeBase.GetViewName();
+ return viewName == null ? null : View(viewName, typeBase.GetViewSchema());
case StoreObjectType.SqlQuery:
- var query = entityType.GetSqlQuery();
- return query == null ? null : SqlQuery(entityType);
+ var query = typeBase.GetSqlQuery();
+ return query == null ? null : SqlQuery(typeBase.FundamentalEntityType);
case StoreObjectType.Function:
- var functionName = entityType.GetFunctionName();
+ var functionName = typeBase.GetFunctionName();
return functionName == null ? null : DbFunction(functionName);
case StoreObjectType.InsertStoredProcedure:
- var insertStoredProcedure = entityType.GetInsertStoredProcedure();
+ var insertStoredProcedure = typeBase.GetInsertStoredProcedure();
return insertStoredProcedure == null || insertStoredProcedure.Name == null
? null
: InsertStoredProcedure(insertStoredProcedure.Name, insertStoredProcedure.Schema);
case StoreObjectType.DeleteStoredProcedure:
- var deleteStoredProcedure = entityType.GetDeleteStoredProcedure();
+ var deleteStoredProcedure = typeBase.GetDeleteStoredProcedure();
return deleteStoredProcedure == null || deleteStoredProcedure.Name == null
? null
: DeleteStoredProcedure(deleteStoredProcedure.Name, deleteStoredProcedure.Schema);
case StoreObjectType.UpdateStoredProcedure:
- var updateStoredProcedure = entityType.GetUpdateStoredProcedure();
+ var updateStoredProcedure = typeBase.GetUpdateStoredProcedure();
return updateStoredProcedure == null || updateStoredProcedure.Name == null
? null
: UpdateStoredProcedure(updateStoredProcedure.Name, updateStoredProcedure.Schema);
diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
index bb3adbeb9ea..aed2c547cac 100644
--- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
+++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
@@ -890,7 +890,7 @@ protected virtual IEnumerable Diff(
t.PropertyMappings.Any(
tm =>
string.Equals(sm.Property.Name, tm.Property.Name, StringComparison.OrdinalIgnoreCase)
- && EntityTypePathEquals(sm.Property.DeclaringEntityType, tm.Property.DeclaringEntityType, c))),
+ && EntityTypePathEquals(sm.Property.DeclaringType, tm.Property.DeclaringType, c))),
(s, t, _) => s.PropertyMappings.Any(
sm =>
t.PropertyMappings.Any(
@@ -902,7 +902,7 @@ protected virtual IEnumerable Diff(
t.PropertyMappings.Any(
tm =>
string.Equals(sm.Property.Name, tm.Property.Name, StringComparison.OrdinalIgnoreCase)
- && EntityTypePathEquals(sm.Property.DeclaringEntityType, tm.Property.DeclaringEntityType, c))),
+ && EntityTypePathEquals(sm.Property.DeclaringType, tm.Property.DeclaringType, c))),
(s, t, _) => ColumnStructureEquals(s, t));
private static bool ColumnStructureEquals(IColumn source, IColumn target)
@@ -933,7 +933,7 @@ private static bool ColumnStructureEquals(IColumn source, IColumn target)
&& source.DefaultValueSql == target.DefaultValueSql;
}
- private static bool EntityTypePathEquals(IEntityType source, IEntityType target, DiffContext diffContext)
+ private static bool EntityTypePathEquals(ITypeBase source, ITypeBase target, DiffContext diffContext)
{
var sourceTable = diffContext.FindTable(source);
var targetTable = diffContext.FindTable(target);
@@ -951,12 +951,29 @@ private static bool EntityTypePathEquals(IEntityType source, IEntityType target,
return false;
}
- var nextSource = sourceTable?.GetRowInternalForeignKeys(source).FirstOrDefault()?.PrincipalEntityType;
- var nextTarget = targetTable?.GetRowInternalForeignKeys(target).FirstOrDefault()?.PrincipalEntityType;
- return (nextSource == null && nextTarget == null)
- || (nextSource != null
- && nextTarget != null
- && EntityTypePathEquals(nextSource, nextTarget, diffContext));
+ if (source is IEntityType sourceEntityType
+ && target is IEntityType targetEntityType)
+ {
+ var nextSource = sourceTable?.GetRowInternalForeignKeys(sourceEntityType).FirstOrDefault()?.PrincipalEntityType;
+ var nextTarget = targetTable?.GetRowInternalForeignKeys(targetEntityType).FirstOrDefault()?.PrincipalEntityType;
+ return (nextSource == null && nextTarget == null)
+ || (nextSource != null
+ && nextTarget != null
+ && EntityTypePathEquals(nextSource, nextTarget, diffContext));
+ }
+
+ if (source is IComplexType sourceComplexType
+ && target is IComplexType targetComplexType)
+ {
+ var nextSource = sourceComplexType.ComplexProperty.DeclaringType;
+ var nextTarget = targetComplexType.ComplexProperty.DeclaringType;
+ return (nextSource == null && nextTarget == null)
+ || (nextSource != null
+ && nextTarget != null
+ && EntityTypePathEquals(nextSource, nextTarget, diffContext));
+ }
+
+ return false;
}
///
@@ -1861,7 +1878,7 @@ private void AddSeedData(IEntityType entityType, Dictionary
- public virtual ITable? FindTable(IEntityType entityType)
- => entityType.GetTableMappings().FirstOrDefault()?.Table;
+ public virtual ITable? FindTable(ITypeBase typeBase)
+ => typeBase.GetTableMappings().FirstOrDefault()?.Table;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index 7b7f43e2888..977720170fc 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -53,14 +53,6 @@ public static string BadSequenceType
public static string CannotChangeWhenOpen
=> GetString("CannotChangeWhenOpen");
- ///
- /// The query contained a new array expression containing non-constant elements, which could not be translated: '{newArrayExpression}'.
- ///
- public static string CannotTranslateNonConstantNewArrayExpression(object? newArrayExpression)
- => string.Format(
- GetString("CannotTranslateNonConstantNewArrayExpression", nameof(newArrayExpression)),
- newArrayExpression);
-
///
/// Can't configure a trigger on entity type '{entityType}', which is in a TPH hierarchy and isn't the root. Configure the trigger on the TPH root entity type '{rootEntityType}' instead.
///
@@ -69,6 +61,14 @@ public static string CannotConfigureTriggerNonRootTphEntity(object? entityType,
GetString("CannotConfigureTriggerNonRootTphEntity", nameof(entityType), nameof(rootEntityType)),
entityType, rootEntityType);
+ ///
+ /// The query contained a new array expression containing non-constant elements, which could not be translated: '{newArrayExpression}'.
+ ///
+ public static string CannotTranslateNonConstantNewArrayExpression(object? newArrayExpression)
+ => string.Format(
+ GetString("CannotTranslateNonConstantNewArrayExpression", nameof(newArrayExpression)),
+ newArrayExpression);
+
///
/// Unable to translate the given 'GroupBy' pattern. Call 'AsEnumerable' before 'GroupBy' to evaluate it client-side.
///
@@ -175,14 +175,6 @@ public static string ConflictingRowValuesSensitive(object? firstEntityType, obje
GetString("ConflictingRowValuesSensitive", nameof(firstEntityType), nameof(secondEntityType), nameof(keyValue), nameof(firstConflictingValue), nameof(secondConflictingValue), nameof(column)),
firstEntityType, secondEntityType, keyValue, firstConflictingValue, secondConflictingValue, column);
- ///
- /// Conflicting type mappings were inferred for column '{column}'.
- ///
- public static string ConflictingTypeMappingsInferredForColumn(object? column)
- => string.Format(
- GetString("ConflictingTypeMappingsInferredForColumn", nameof(column)),
- column);
-
///
/// A seed entity for entity type '{entityType}' has the same key value as another seed entity mapped to the same table '{table}', but have different values for the column '{column}'. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting values.
///
@@ -199,6 +191,14 @@ public static string ConflictingSeedValuesSensitive(object? entityType, object?
GetString("ConflictingSeedValuesSensitive", nameof(entityType), nameof(keyValue), nameof(table), nameof(column), nameof(firstValue), nameof(secondValue)),
entityType, keyValue, table, column, firstValue, secondValue);
+ ///
+ /// Conflicting type mappings were inferred for column '{column}'.
+ ///
+ public static string ConflictingTypeMappingsInferredForColumn(object? column)
+ => string.Format(
+ GetString("ConflictingTypeMappingsInferredForColumn", nameof(column)),
+ column);
+
///
/// {numSortOrderProperties} values were provided in CreateIndexOperations.IsDescending, but the operation has {numColumns} columns.
///
@@ -630,15 +630,7 @@ public static string DuplicateSeedDataSensitive(object? entityType, object? keyV
entityType, keyValue, table);
///
- /// Exactly one of '{param1}', '{param2}' or '{param3}' must be set.
- ///
- public static string OneOfThreeValuesMustBeSet(object? param1, object? param2, object? param3)
- => string.Format(
- GetString("OneOfThreeValuesMustBeSet", nameof(param1), nameof(param2), nameof(param3)),
- param1, param2, param3);
-
- ///
- /// Empty collections are not supported as constant query roots.
+ /// Empty collections are not supported as inline query roots.
///
public static string EmptyCollectionNotSupportedAsInlineQueryRoot
=> GetString("EmptyCollectionNotSupportedAsInlineQueryRoot");
@@ -1424,12 +1416,12 @@ public static string NullTypeMappingInSqlTree(object? sqlExpression)
sqlExpression);
///
- /// Entity type '{entityType}' is an optional dependent using table sharing and containing other dependents without any required non shared property to identify whether the entity exists. If all nullable properties contain a null value in database then an object instance won't be created in the query causing nested dependent's values to be lost. Add a required property to create instances with null values for other properties or mark the incoming navigation as required to always create an instance.
+ /// Exactly one of '{param1}', '{param2}' or '{param3}' must be set.
///
- public static string OptionalDependentWithDependentWithoutIdentifyingProperty(object? entityType)
+ public static string OneOfThreeValuesMustBeSet(object? param1, object? param2, object? param3)
=> string.Format(
- GetString("OptionalDependentWithDependentWithoutIdentifyingProperty", nameof(entityType)),
- entityType);
+ GetString("OneOfThreeValuesMustBeSet", nameof(param1), nameof(param2), nameof(param3)),
+ param1, param2, param3);
///
/// Only constants are supported inside inline collection query roots.
@@ -1437,6 +1429,14 @@ public static string OptionalDependentWithDependentWithoutIdentifyingProperty(ob
public static string OnlyConstantsSupportedInInlineCollectionQueryRoots
=> GetString("OnlyConstantsSupportedInInlineCollectionQueryRoots");
+ ///
+ /// Entity type '{entityType}' is an optional dependent using table sharing and containing other dependents without any required non shared property to identify whether the entity exists. If all nullable properties contain a null value in database then an object instance won't be created in the query causing nested dependent's values to be lost. Add a required property to create instances with null values for other properties or mark the incoming navigation as required to always create an instance.
+ ///
+ public static string OptionalDependentWithDependentWithoutIdentifyingProperty(object? entityType)
+ => string.Format(
+ GetString("OptionalDependentWithDependentWithoutIdentifyingProperty", nameof(entityType)),
+ entityType);
+
///
/// Cannot use the value provided for parameter '{parameter}' because it isn't assignable to type object[].
///
@@ -1535,6 +1535,14 @@ public static string SqlQueryUnmappedType(object? elementType)
GetString("SqlQueryUnmappedType", nameof(elementType)),
elementType);
+ ///
+ /// The foreign key column '{fkColumnName}' has '{fkColumnType}' values which cannot be compared to the '{pkColumnType}' values of the associated principal key column '{pkColumnName}'. To use 'SaveChanges` or 'SaveChangesAsync', foreign key column types must be comparable with principal key column types.
+ ///
+ public static string StoredKeyTypesNotConvertable(object? fkColumnName, object? fkColumnType, object? pkColumnType, object? pkColumnName)
+ => string.Format(
+ GetString("StoredKeyTypesNotConvertable", nameof(fkColumnName), nameof(fkColumnType), nameof(pkColumnType), nameof(pkColumnName)),
+ fkColumnName, fkColumnType, pkColumnType, pkColumnName);
+
///
/// Current value parameter '{parameter}' is not allowed on delete stored procedure '{sproc}'. Use HasOriginalValueParameter() instead.
///
@@ -1744,19 +1752,19 @@ public static string StoredProcedureResultColumnParameterConflict(object? entity
entityType, property, sproc);
///
- /// Stored procedure '{sproc}' was configured with a rows affected output parameter or return value, but a valid value was not found when executing the procedure.
+ /// A rows affected parameter, result column or return value cannot be configured on stored procedure '{sproc}' because it is used for insertion. Rows affected values are only allowed on stored procedures performing updating or deletion.
///
- public static string StoredProcedureRowsAffectedNotPopulated(object? sproc)
+ public static string StoredProcedureRowsAffectedForInsert(object? sproc)
=> string.Format(
- GetString("StoredProcedureRowsAffectedNotPopulated", nameof(sproc)),
+ GetString("StoredProcedureRowsAffectedForInsert", nameof(sproc)),
sproc);
///
- /// A rows affected parameter, result column or return value cannot be configured on stored procedure '{sproc}' because it is used for insertion. Rows affected values are only allowed on stored procedures performing updating or deletion.
+ /// Stored procedure '{sproc}' was configured with a rows affected output parameter or return value, but a valid value was not found when executing the procedure.
///
- public static string StoredProcedureRowsAffectedForInsert(object? sproc)
+ public static string StoredProcedureRowsAffectedNotPopulated(object? sproc)
=> string.Format(
- GetString("StoredProcedureRowsAffectedForInsert", nameof(sproc)),
+ GetString("StoredProcedureRowsAffectedNotPopulated", nameof(sproc)),
sproc);
///
@@ -1799,14 +1807,6 @@ public static string StoredProcedureUnmapped(object? entityType)
GetString("StoredProcedureUnmapped", nameof(entityType)),
entityType);
- ///
- /// The foreign key column '{fkColumnName}' has '{fkColumnType}' values which cannot be compared to the '{pkColumnType}' values of the associated principal key column '{pkColumnName}'. To use 'SaveChanges` or 'SaveChangesAsync', foreign key column types must be comparable with principal key column types.
- ///
- public static string StoredKeyTypesNotConvertable(object? fkColumnName, object? fkColumnType, object? pkColumnType, object? pkColumnName)
- => string.Format(
- GetString("StoredKeyTypesNotConvertable", nameof(fkColumnName), nameof(fkColumnType), nameof(pkColumnType), nameof(pkColumnName)),
- fkColumnName, fkColumnType, pkColumnType, pkColumnName);
-
///
/// Expression type '{expressionType}' does not implement proper cloning logic. Every expression derived from '{tableExpressionBase}' must implement '{clonableTableExpressionBase}' interface or have it's cloning logic added to the '{cloningExpressionVisitor}' inside '{selectExpression}'.
///
@@ -1994,7 +1994,7 @@ public static string UnsupportedOperatorForSqlExpression(object? nodeType, objec
nodeType, expressionType);
///
- /// No relational type mapping can be found for property '{entity}.{property}' and the current provider doesn't specify a default store type for the properties of type '{clrType}'.
+ /// No relational type mapping can be found for property '{entity}.{property}' and the current provider doesn't specify a default store type for the properties of type '{clrType}'.
///
public static string UnsupportedPropertyType(object? entity, object? property, object? clrType)
=> string.Format(
@@ -2018,7 +2018,7 @@ public static string UnsupportedType(object? clrType)
clrType);
///
- /// The database operation was expected to affect {expectedRows} row(s), but actually affected {actualRows} row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
+ /// The database operation was expected to affect {expectedRows} row(s), but actually affected {actualRows} row(s); data may have been modified or deleted since entities were loaded. See https://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
///
public static string UpdateConcurrencyException(object? expectedRows, object? actualRows)
=> string.Format(
@@ -2120,7 +2120,7 @@ private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.EntityFrameworkCore.Properties.RelationalStrings", typeof(RelationalResources).Assembly);
///
- /// An ambient transaction has been detected, but the current provider does not support ambient transactions. See http://go.microsoft.com/fwlink/?LinkId=800142
+ /// An ambient transaction has been detected, but the current provider does not support ambient transactions. See https://go.microsoft.com/fwlink/?LinkId=800142
///
public static EventDefinition LogAmbientTransaction(IDiagnosticsLogger logger)
{
@@ -3535,31 +3535,6 @@ public static EventDefinition LogOptionalDependentWithoutIdentifyingProp
return (EventDefinition)definition;
}
- ///
- /// The entity type '{entityType}' is mapped to the stored procedure '{sproc}', but the concurrency token '{token}' is not mapped to any original value parameter.
- ///
- public static EventDefinition LogStoredProcedureConcurrencyTokenNotMapped(IDiagnosticsLogger logger)
- {
- var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogStoredProcedureConcurrencyTokenNotMapped;
- if (definition == null)
- {
- definition = NonCapturingLazyInitializer.EnsureInitialized(
- ref ((RelationalLoggingDefinitions)logger.Definitions).LogStoredProcedureConcurrencyTokenNotMapped,
- logger,
- static logger => new EventDefinition(
- logger.Options,
- RelationalEventId.StoredProcedureConcurrencyTokenNotMapped,
- LogLevel.Warning,
- "RelationalEventId.StoredProcedureConcurrencyTokenNotMapped",
- level => LoggerMessage.Define(
- level,
- RelationalEventId.StoredProcedureConcurrencyTokenNotMapped,
- _resourceManager.GetString("LogStoredProcedureConcurrencyTokenNotMapped")!)));
- }
-
- return (EventDefinition)definition;
- }
-
///
/// Possible unintended use of method 'Equals' for arguments '{left}' and '{right}' of different types in a query. This comparison will always return false.
///
@@ -3760,6 +3735,31 @@ public static EventDefinition LogRollingBackTransaction(IDiagnosticsLogger logge
return (EventDefinition)definition;
}
+ ///
+ /// The entity type '{entityType}' is mapped to the stored procedure '{sproc}', but the concurrency token '{token}' is not mapped to any original value parameter.
+ ///
+ public static EventDefinition LogStoredProcedureConcurrencyTokenNotMapped(IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogStoredProcedureConcurrencyTokenNotMapped;
+ if (definition == null)
+ {
+ definition = NonCapturingLazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogStoredProcedureConcurrencyTokenNotMapped,
+ logger,
+ static logger => new EventDefinition(
+ logger.Options,
+ RelationalEventId.StoredProcedureConcurrencyTokenNotMapped,
+ LogLevel.Warning,
+ "RelationalEventId.StoredProcedureConcurrencyTokenNotMapped",
+ level => LoggerMessage.Define(
+ level,
+ RelationalEventId.StoredProcedureConcurrencyTokenNotMapped,
+ _resourceManager.GetString("LogStoredProcedureConcurrencyTokenNotMapped")!)));
+ }
+
+ return (EventDefinition)definition;
+ }
+
///
/// The entity type '{entityType}' is using the table per concrete type mapping strategy, but property '{property}' is configured with an incompatible database-generated default. Configure a compatible value generation strategy if available, or use non-generated key values.
///
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx
index d212c1beb9e..00203a4f7eb 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.resx
+++ b/src/EFCore.Relational/Properties/RelationalStrings.resx
@@ -1,17 +1,17 @@

-
@@ -130,12 +130,12 @@
The instance of DbConnection is currently in use. The connection can only be changed when the existing connection is not being used.
-
- The query contained a new array expression containing non-constant elements, which could not be translated: '{newArrayExpression}'.
-
Can't configure a trigger on entity type '{entityType}', which is in a TPH hierarchy and isn't the root. Configure the trigger on the TPH root entity type '{rootEntityType}' instead.
+
+ The query contained a new array expression containing non-constant elements, which could not be translated: '{newArrayExpression}'.
+
Unable to translate the given 'GroupBy' pattern. Call 'AsEnumerable' before 'GroupBy' to evaluate it client-side.
@@ -178,15 +178,15 @@
Instances of entity types '{firstEntityType}' and '{secondEntityType}' are mapped to the same row with the key value '{keyValue}', but have different property values '{firstConflictingValue}' and '{secondConflictingValue}' for the column '{column}'.
-
- Conflicting type mappings were inferred for column '{column}'.
-
A seed entity for entity type '{entityType}' has the same key value as another seed entity mapped to the same table '{table}', but have different values for the column '{column}'. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting values.
A seed entity for entity type '{entityType}' has the same key value {keyValue} as another seed entity mapped to the same table '{table}', but have different values for the column '{column}' - '{firstValue}', '{secondValue}'.
+
+ Conflicting type mappings were inferred for column '{column}'.
+
{numSortOrderProperties} values were provided in CreateIndexOperations.IsDescending, but the operation has {numColumns} columns.
@@ -349,9 +349,6 @@
A seed entity for entity type '{entityType}' has the same key value {keyValue} as another seed entity mapped to the same table '{table}'. Key values should be unique across seed entities.
-
- Exactly one of '{param1}', '{param2}' or '{param3}' must be set.
-
Empty collections are not supported as inline query roots.
@@ -560,7 +557,7 @@
Queries performing '{method}' operation must have a deterministic sort order. Rewrite the query to apply an 'OrderBy' operation on the sequence before calling '{method}'.
- An ambient transaction has been detected, but the current provider does not support ambient transactions. See http://go.microsoft.com/fwlink/?LinkId=800142
+ An ambient transaction has been detected, but the current provider does not support ambient transactions. See https://go.microsoft.com/fwlink/?LinkId=800142
Warning RelationalEventId.AmbientTransactionWarning
@@ -956,12 +953,15 @@
Expression '{sqlExpression}' in the SQL tree does not have a type mapping assigned.
-
- Entity type '{entityType}' is an optional dependent using table sharing and containing other dependents without any required non shared property to identify whether the entity exists. If all nullable properties contain a null value in database then an object instance won't be created in the query causing nested dependent's values to be lost. Add a required property to create instances with null values for other properties or mark the incoming navigation as required to always create an instance.
+
+ Exactly one of '{param1}', '{param2}' or '{param3}' must be set.
Only constants are supported inside inline collection query roots.
+
+ Entity type '{entityType}' is an optional dependent using table sharing and containing other dependents without any required non shared property to identify whether the entity exists. If all nullable properties contain a null value in database then an object instance won't be created in the query causing nested dependent's values to be lost. Add a required property to create instances with null values for other properties or mark the incoming navigation as required to always create an instance.
+
Cannot use the value provided for parameter '{parameter}' because it isn't assignable to type object[].
@@ -1188,7 +1188,7 @@
The current provider doesn't have a store type mapping for properties of type '{clrType}'.
- The database operation was expected to affect {expectedRows} row(s), but actually affected {actualRows} row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
+ The database operation was expected to affect {expectedRows} row(s), but actually affected {actualRows} row(s); data may have been modified or deleted since entities were loaded. See https://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
The number of key column types ({typesCount}) doesn't match the number of key columns ({columnsCount}) for the data modification operation on '{table}'. Provide the same number of key column types and key columns.
diff --git a/src/EFCore.Relational/Query/EntityProjectionExpression.cs b/src/EFCore.Relational/Query/EntityProjectionExpression.cs
index de75b1589d0..eddac5e66aa 100644
--- a/src/EFCore.Relational/Query/EntityProjectionExpression.cs
+++ b/src/EFCore.Relational/Query/EntityProjectionExpression.cs
@@ -154,8 +154,8 @@ public virtual EntityProjectionExpression UpdateEntityType(IEntityType derivedTy
var propertyExpressionMap = new Dictionary();
foreach (var (property, columnExpression) in _propertyExpressionMap)
{
- if (derivedType.IsAssignableFrom(property.DeclaringEntityType)
- || property.DeclaringEntityType.IsAssignableFrom(derivedType))
+ if (derivedType.IsAssignableFrom(property.DeclaringType)
+ || property.DeclaringType.IsAssignableFrom(derivedType))
{
propertyExpressionMap[property] = columnExpression;
}
@@ -193,8 +193,8 @@ public virtual EntityProjectionExpression UpdateEntityType(IEntityType derivedTy
/// A column which is a SQL representation of the property.
public virtual ColumnExpression BindProperty(IProperty property)
{
- if (!EntityType.IsAssignableFrom(property.DeclaringEntityType)
- && !property.DeclaringEntityType.IsAssignableFrom(EntityType))
+ if (!EntityType.IsAssignableFrom(property.DeclaringType)
+ && !property.DeclaringType.IsAssignableFrom(EntityType))
{
throw new InvalidOperationException(
RelationalStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName()));
diff --git a/src/EFCore.Relational/Query/Internal/CollateTranslator.cs b/src/EFCore.Relational/Query/Internal/CollateTranslator.cs
index faebaa9af3e..10c8183a506 100644
--- a/src/EFCore.Relational/Query/Internal/CollateTranslator.cs
+++ b/src/EFCore.Relational/Query/Internal/CollateTranslator.cs
@@ -14,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal;
public class CollateTranslator : IMethodCallTranslator
{
private static readonly MethodInfo MethodInfo
- = typeof(RelationalDbFunctionsExtensions).GetMethod(nameof(RelationalDbFunctionsExtensions.Collate))!;
+ = typeof(RelationalDbFunctionExtensions).GetMethod(nameof(RelationalDbFunctionExtensions.Collate))!;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Query/JsonQueryExpression.cs b/src/EFCore.Relational/Query/JsonQueryExpression.cs
index 2e22dccab20..1d5d78bb9f6 100644
--- a/src/EFCore.Relational/Query/JsonQueryExpression.cs
+++ b/src/EFCore.Relational/Query/JsonQueryExpression.cs
@@ -100,8 +100,8 @@ public override ExpressionType NodeType
///
public virtual SqlExpression BindProperty(IProperty property)
{
- if (!EntityType.IsAssignableFrom(property.DeclaringEntityType)
- && !property.DeclaringEntityType.IsAssignableFrom(EntityType))
+ if (!EntityType.IsAssignableFrom(property.DeclaringType)
+ && !property.DeclaringType.IsAssignableFrom(EntityType))
{
throw new InvalidOperationException(
RelationalStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName()));
diff --git a/src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilter.cs b/src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilter.cs
index 29df9899207..6ceab3daa0d 100644
--- a/src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilter.cs
+++ b/src/EFCore.Relational/Query/RelationalEvaluatableExpressionFilter.cs
@@ -45,7 +45,7 @@ public override bool IsEvaluatableExpression(Expression expression, IModel model
return false;
}
- if (method.DeclaringType == typeof(RelationalDbFunctionsExtensions))
+ if (method.DeclaringType == typeof(RelationalDbFunctionExtensions))
{
return false;
}
diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
index 36c3df4a2eb..4aaf4725137 100644
--- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
+++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
@@ -309,9 +309,9 @@ internal SelectExpression(IEntityType entityType, ISqlExpressionFactory sqlExpre
for (var j = 0; j < properties.Length; j++)
{
var property = properties[j];
- var projection = property.DeclaringEntityType.IsAssignableFrom(et)
+ var projection = property.DeclaringType.IsAssignableFrom(et)
? CreateColumnExpression(
- property, table, tableReferenceExpression, property.DeclaringEntityType != entityType)
+ property, table, tableReferenceExpression, property.DeclaringType != entityType)
: (SqlExpression)sqlExpressionFactory.Constant(
null, property.ClrType.MakeNullable(), property.GetRelationalTypeMapping());
selectExpression._projection.Add(new ProjectionExpression(projection, propertyNames[j]));
diff --git a/src/EFCore.Relational/Storage/RelationalTypeMappingSourceExtensions.cs b/src/EFCore.Relational/Storage/RelationalTypeMappingSourceExtensions.cs
index e283a05b2fe..90ec0760337 100644
--- a/src/EFCore.Relational/Storage/RelationalTypeMappingSourceExtensions.cs
+++ b/src/EFCore.Relational/Storage/RelationalTypeMappingSourceExtensions.cs
@@ -63,7 +63,7 @@ public static RelationalTypeMapping GetMapping(
throw new InvalidOperationException(
RelationalStrings.UnsupportedPropertyType(
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
property.ClrType.ShortDisplayName()));
}
diff --git a/src/EFCore.Relational/Storage/ValueConversion/RelationalConverterMappingHints.cs b/src/EFCore.Relational/Storage/ValueConversion/RelationalConverterMappingHints.cs
index 1b94d84e0ec..70bb0489ff5 100644
--- a/src/EFCore.Relational/Storage/ValueConversion/RelationalConverterMappingHints.cs
+++ b/src/EFCore.Relational/Storage/ValueConversion/RelationalConverterMappingHints.cs
@@ -30,7 +30,7 @@ public RelationalConverterMappingHints(
int? scale = null,
bool? unicode = null,
bool? fixedLength = null,
- Func? valueGeneratorFactory = null,
+ Func? valueGeneratorFactory = null,
DbType? dbType = null)
: base(size, precision, scale, unicode, valueGeneratorFactory)
{
@@ -55,7 +55,7 @@ public RelationalConverterMappingHints(
int? scale,
bool? unicode,
bool? fixedLength,
- Func? valueGeneratorFactory)
+ Func? valueGeneratorFactory)
: base(size, precision, scale, unicode, valueGeneratorFactory)
{
IsFixedLength = fixedLength;
diff --git a/src/EFCore.Relational/Update/ModificationCommand.cs b/src/EFCore.Relational/Update/ModificationCommand.cs
index 631310a3727..5ef656d7d1b 100644
--- a/src/EFCore.Relational/Update/ModificationCommand.cs
+++ b/src/EFCore.Relational/Update/ModificationCommand.cs
@@ -830,7 +830,7 @@ private static void InitializeSharedColumns(
{
foreach (var columnMapping in tableMapping.ColumnMappings)
{
- if (columnMapping.Property.DeclaringEntityType.IsMappedToJson())
+ if (columnMapping.Property.DeclaringType.IsMappedToJson())
{
continue;
}
diff --git a/src/EFCore.Relational/ValueGeneration/RelationalValueGeneratorSelector.cs b/src/EFCore.Relational/ValueGeneration/RelationalValueGeneratorSelector.cs
index fa93bf5f5af..9e8d9e2844f 100644
--- a/src/EFCore.Relational/ValueGeneration/RelationalValueGeneratorSelector.cs
+++ b/src/EFCore.Relational/ValueGeneration/RelationalValueGeneratorSelector.cs
@@ -42,11 +42,11 @@ public RelationalValueGeneratorSelector(ValueGeneratorSelectorDependencies depen
}
///
- protected override ValueGenerator? FindForType(IProperty property, IEntityType entityType, Type clrType)
+ protected override ValueGenerator? FindForType(IProperty property, ITypeBase typeBase, Type clrType)
{
- if (entityType.IsMappedToJson() && property.IsOrdinalKeyProperty())
+ if (typeBase.IsMappedToJson() && property.IsOrdinalKeyProperty())
{
- return _numberFactory.Create(property, entityType);
+ return _numberFactory.Create(property, typeBase);
}
if (property.ValueGenerated != ValueGenerated.Never)
@@ -56,7 +56,7 @@ public RelationalValueGeneratorSelector(ValueGeneratorSelectorDependencies depen
|| clrType == typeof(float)
|| clrType == typeof(double))
{
- return _numberFactory.Create(property, entityType);
+ return _numberFactory.Create(property, typeBase);
}
if (clrType == typeof(DateTime))
@@ -88,6 +88,6 @@ public RelationalValueGeneratorSelector(ValueGeneratorSelectorDependencies depen
}
}
- return base.FindForType(property, entityType, clrType);
+ return base.FindForType(property, typeBase, clrType);
}
}
diff --git a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs
index 90783024415..bb46a3e6be7 100644
--- a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs
+++ b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs
@@ -167,7 +167,7 @@ public override IReadOnlyList GenerateFluentApiCalls(
{
var fragments = new List(base.GenerateFluentApiCalls(property, annotations));
- if (GenerateValueGenerationStrategy(annotations, property.DeclaringEntityType.Model, onModel: false) is MethodCallCodeFragment
+ if (GenerateValueGenerationStrategy(annotations, property.DeclaringType.Model, onModel: false) is MethodCallCodeFragment
valueGenerationStrategy)
{
fragments.Add(valueGenerationStrategy);
@@ -307,7 +307,7 @@ protected override bool IsHandledByConvention(IProperty property, IAnnotation an
{
if (annotation.Name == SqlServerAnnotationNames.ValueGenerationStrategy)
{
- return (SqlServerValueGenerationStrategy)annotation.Value! == property.DeclaringEntityType.Model.GetValueGenerationStrategy();
+ return (SqlServerValueGenerationStrategy)annotation.Value! == property.DeclaringType.Model.GetValueGenerationStrategy();
}
return base.IsHandledByConvention(property, annotation);
diff --git a/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs b/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs
index 9da54e957a7..64642e4a680 100644
--- a/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs
+++ b/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs
@@ -27,7 +27,7 @@ public static void DecimalTypeKeyWarning(
if (diagnostics.ShouldLog(definition))
{
- definition.Log(diagnostics, property.Name, property.DeclaringEntityType.DisplayName());
+ definition.Log(diagnostics, property.Name, property.DeclaringType.DisplayName());
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -47,7 +47,7 @@ private static string DecimalTypeKeyWarning(EventDefinitionBase definition, Even
var p = (PropertyEventData)payload;
return d.GenerateMessage(
p.Property.Name,
- p.Property.DeclaringEntityType.DisplayName());
+ p.Property.DeclaringType.DisplayName());
}
///
@@ -64,7 +64,7 @@ public static void DecimalTypeDefaultWarning(
if (diagnostics.ShouldLog(definition))
{
- definition.Log(diagnostics, property.Name, property.DeclaringEntityType.DisplayName());
+ definition.Log(diagnostics, property.Name, property.DeclaringType.DisplayName());
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -84,7 +84,7 @@ private static string DecimalTypeDefaultWarning(EventDefinitionBase definition,
var p = (PropertyEventData)payload;
return d.GenerateMessage(
p.Property.Name,
- p.Property.DeclaringEntityType.DisplayName());
+ p.Property.DeclaringType.DisplayName());
}
///
@@ -101,7 +101,7 @@ public static void ByteIdentityColumnWarning(
if (diagnostics.ShouldLog(definition))
{
- definition.Log(diagnostics, property.Name, property.DeclaringEntityType.DisplayName());
+ definition.Log(diagnostics, property.Name, property.DeclaringType.DisplayName());
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -121,7 +121,7 @@ private static string ByteIdentityColumnWarning(EventDefinitionBase definition,
var p = (PropertyEventData)payload;
return d.GenerateMessage(
p.Property.Name,
- p.Property.DeclaringEntityType.DisplayName());
+ p.Property.DeclaringType.DisplayName());
}
///
@@ -142,7 +142,7 @@ public static void ConflictingValueGenerationStrategiesWarning(
{
definition.Log(
diagnostics, sqlServerValueGenerationStrategy.ToString(), otherValueGenerationStrategy,
- property.Name, property.DeclaringEntityType.DisplayName());
+ property.Name, property.DeclaringType.DisplayName());
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -166,7 +166,7 @@ private static string ConflictingValueGenerationStrategiesWarning(EventDefinitio
p.SqlServerValueGenerationStrategy.ToString(),
p.OtherValueGenerationStrategy,
p.Property.Name,
- p.Property.DeclaringEntityType.DisplayName());
+ p.Property.DeclaringType.DisplayName());
}
///
diff --git a/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePropertyBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePropertyBuilderExtensions.cs
new file mode 100644
index 00000000000..3e55e93207a
--- /dev/null
+++ b/src/EFCore.SqlServer/Extensions/SqlServerComplexTypePropertyBuilderExtensions.cs
@@ -0,0 +1,258 @@
+// 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;
+
+///
+/// SQL Server specific extension methods for .
+///
+///
+/// See Modeling entity types and relationships, and
+/// Accessing SQL Server and SQL Azure databases with EF Core
+/// for more information and examples.
+///
+public static class SqlServerComplexTypePropertyBuilderExtensions
+{
+ ///
+ /// Configures the key property to use a sequence-based hi-lo pattern to generate values for new entities,
+ /// when targeting SQL Server. This method sets the property to be .
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The name of the sequence.
+ /// The schema of the sequence.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseHiLo(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? name = null,
+ string? schema = null)
+ {
+ Check.NullButNotEmpty(name, nameof(name));
+ Check.NullButNotEmpty(schema, nameof(schema));
+
+ var property = propertyBuilder.Metadata;
+
+ name ??= SqlServerModelExtensions.DefaultHiLoSequenceName;
+
+ var model = property.DeclaringType.Model;
+
+ if (model.FindSequence(name, schema) == null)
+ {
+ model.AddSequence(name, schema).IncrementBy = 10;
+ }
+
+ property.SetValueGenerationStrategy(SqlServerValueGenerationStrategy.SequenceHiLo);
+ property.SetHiLoSequenceName(name);
+ property.SetHiLoSequenceSchema(schema);
+ property.SetIdentitySeed(null);
+ property.SetIdentityIncrement(null);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the key property to use a sequence-based hi-lo pattern to generate values for new entities,
+ /// when targeting SQL Server. This method sets the property to be .
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The name of the sequence.
+ /// The schema of the sequence.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseHiLo(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? name = null,
+ string? schema = null)
+ => (ComplexTypePropertyBuilder)UseHiLo((ComplexTypePropertyBuilder)propertyBuilder, name, schema);
+
+ ///
+ /// Configures the key property to use a sequence-based key value generation pattern to generate values for new entities,
+ /// when targeting SQL Server. This method sets the property to be .
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The name of the sequence.
+ /// The schema of the sequence.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseSequence(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? name = null,
+ string? schema = null)
+ {
+ Check.NullButNotEmpty(name, nameof(name));
+ Check.NullButNotEmpty(schema, nameof(schema));
+
+ var property = propertyBuilder.Metadata;
+
+ property.SetValueGenerationStrategy(SqlServerValueGenerationStrategy.Sequence);
+ property.SetSequenceName(name);
+ property.SetSequenceSchema(schema);
+ property.SetHiLoSequenceName(null);
+ property.SetHiLoSequenceSchema(null);
+ property.SetIdentitySeed(null);
+ property.SetIdentityIncrement(null);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the key property to use a sequence-based key value generation pattern to generate values for new entities,
+ /// when targeting SQL Server. This method sets the property to be .
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The name of the sequence.
+ /// The schema of the sequence.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseSequence(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ string? name = null,
+ string? schema = null)
+ => (ComplexTypePropertyBuilder)UseSequence((ComplexTypePropertyBuilder)propertyBuilder, name, schema);
+
+ ///
+ /// Configures the key property to use the SQL Server IDENTITY feature to generate values for new entities,
+ /// when targeting SQL Server. This method sets the property to be .
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The value that is used for the very first row loaded into the table.
+ /// The incremental value that is added to the identity value of the previous row that was loaded.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseIdentityColumn(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ long seed = 1,
+ int increment = 1)
+ {
+ var property = propertyBuilder.Metadata;
+ property.SetValueGenerationStrategy(SqlServerValueGenerationStrategy.IdentityColumn);
+ property.SetIdentitySeed(seed);
+ property.SetIdentityIncrement(increment);
+ property.SetHiLoSequenceName(null);
+ property.SetHiLoSequenceSchema(null);
+ property.SetSequenceName(null);
+ property.SetSequenceSchema(null);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the key property to use the SQL Server IDENTITY feature to generate values for new entities,
+ /// when targeting SQL Server. This method sets the property to be .
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples.
+ ///
+ /// The builder for the property being configured.
+ /// The value that is used for the very first row loaded into the table.
+ /// The incremental value that is added to the identity value of the previous row that was loaded.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseIdentityColumn(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ int seed,
+ int increment = 1)
+ => propertyBuilder.UseIdentityColumn((long)seed, increment);
+
+ ///
+ /// Configures the key property to use the SQL Server IDENTITY feature to generate values for new entities,
+ /// when targeting SQL Server. This method sets the property to be .
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The value that is used for the very first row loaded into the table.
+ /// The incremental value that is added to the identity value of the previous row that was loaded.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseIdentityColumn(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ long seed = 1,
+ int increment = 1)
+ => (ComplexTypePropertyBuilder)UseIdentityColumn((ComplexTypePropertyBuilder)propertyBuilder, seed, increment);
+
+ ///
+ /// Configures the key property to use the SQL Server IDENTITY feature to generate values for new entities,
+ /// when targeting SQL Server. This method sets the property to be .
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples.
+ ///
+ /// The type of the property being configured.
+ /// The builder for the property being configured.
+ /// The value that is used for the very first row loaded into the table.
+ /// The incremental value that is added to the identity value of the previous row that was loaded.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder UseIdentityColumn(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ int seed,
+ int increment = 1)
+ => (ComplexTypePropertyBuilder)UseIdentityColumn((ComplexTypePropertyBuilder)propertyBuilder, (long)seed, increment);
+
+ ///
+ /// Configures whether the property's column is created as sparse when targeting SQL Server.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples. Also see
+ /// Sparse columns for
+ /// general information on SQL Server sparse columns.
+ ///
+ /// The builder for the property being configured.
+ /// A value indicating whether the property's column is created as sparse.
+ /// A builder to further configure the property.
+ public static ComplexTypePropertyBuilder IsSparse(this ComplexTypePropertyBuilder propertyBuilder, bool sparse = true)
+ {
+ propertyBuilder.Metadata.SetIsSparse(sparse);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures whether the property's column is created as sparse when targeting SQL Server.
+ ///
+ ///
+ /// See Modeling entity types and relationships, and
+ /// Accessing SQL Server and SQL Azure databases with EF Core
+ /// for more information and examples. Also see
+ /// Sparse columns for
+ /// general information on SQL Server sparse columns.
+ ///
+ /// The builder for the property being configured.
+ /// A value indicating whether the property's column is created as sparse.
+ /// A builder to further configure the property.
+ public static ComplexTypePropertyBuilder IsSparse(
+ this ComplexTypePropertyBuilder propertyBuilder,
+ bool sparse = true)
+ => (ComplexTypePropertyBuilder)IsSparse((ComplexTypePropertyBuilder)propertyBuilder, sparse);
+}
diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs
index 45d9696492f..11d8e694a1c 100644
--- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs
+++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyBuilderExtensions.cs
@@ -41,7 +41,7 @@ public static PropertyBuilder UseHiLo(
name ??= SqlServerModelExtensions.DefaultHiLoSequenceName;
- var model = property.DeclaringEntityType.Model;
+ var model = property.DeclaringType.Model;
if (model.FindSequence(name, schema) == null)
{
@@ -107,7 +107,7 @@ public static PropertyBuilder UseHiLo(
return name == null
? null
- : propertyBuilder.Metadata.DeclaringEntityType.Model.Builder.HasSequence(name, schema, fromDataAnnotation);
+ : propertyBuilder.Metadata.DeclaringType.Model.Builder.HasSequence(name, schema, fromDataAnnotation);
}
///
@@ -220,7 +220,7 @@ public static PropertyBuilder UseSequence(
return name == null
? null
- : propertyBuilder.Metadata.DeclaringEntityType.Model.Builder.HasSequence(name, schema, fromDataAnnotation);
+ : propertyBuilder.Metadata.DeclaringType.Model.Builder.HasSequence(name, schema, fromDataAnnotation);
}
///
diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs
index 890c57a1005..5251dbdabbf 100644
--- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs
+++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs
@@ -142,7 +142,7 @@ public static void SetHiLoSequenceSchema(this IMutableProperty property, string?
/// The sequence to use, or if no sequence exists in the model.
public static IReadOnlySequence? FindHiLoSequence(this IReadOnlyProperty property)
{
- var model = property.DeclaringEntityType.Model;
+ var model = property.DeclaringType.Model;
var sequenceName = property.GetHiLoSequenceName()
?? model.GetHiLoSequenceName();
@@ -161,7 +161,7 @@ public static void SetHiLoSequenceSchema(this IMutableProperty property, string?
/// The sequence to use, or if no sequence exists in the model.
public static IReadOnlySequence? FindHiLoSequence(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
{
- var model = property.DeclaringEntityType.Model;
+ var model = property.DeclaringType.Model;
var sequenceName = property.GetHiLoSequenceName(storeObject)
?? model.GetHiLoSequenceName();
@@ -314,7 +314,7 @@ public static void SetSequenceSchema(this IMutableProperty property, string? sch
/// The sequence to use, or if no sequence exists in the model.
public static IReadOnlySequence? FindSequence(this IReadOnlyProperty property)
{
- var model = property.DeclaringEntityType.Model;
+ var model = property.DeclaringType.Model;
var sequenceName = property.GetSequenceName()
?? model.GetSequenceNameSuffix();
@@ -333,7 +333,7 @@ public static void SetSequenceSchema(this IMutableProperty property, string? sch
/// The sequence to use, or if no sequence exists in the model.
public static IReadOnlySequence? FindSequence(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
{
- var model = property.DeclaringEntityType.Model;
+ var model = property.DeclaringType.Model;
var sequenceName = property.GetSequenceName(storeObject)
?? model.GetSequenceNameSuffix();
@@ -412,7 +412,7 @@ public static void SetSequenceSchema(this IMutableProperty property, string? sch
var sharedProperty = property.FindSharedStoreObjectRootProperty(storeObject);
return sharedProperty == null
- ? property.DeclaringEntityType.Model.GetIdentitySeed()
+ ? property.DeclaringType.Model.GetIdentitySeed()
: sharedProperty.GetIdentitySeed(storeObject);
}
@@ -539,7 +539,7 @@ public static void SetIdentitySeed(this IMutableRelationalPropertyOverrides over
=> (property is RuntimeProperty)
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
: (int?)property[SqlServerAnnotationNames.IdentityIncrement]
- ?? property.DeclaringEntityType.Model.GetIdentityIncrement();
+ ?? property.DeclaringType.Model.GetIdentityIncrement();
///
/// Returns the identity increment.
@@ -568,7 +568,7 @@ public static void SetIdentitySeed(this IMutableRelationalPropertyOverrides over
var sharedProperty = property.FindSharedStoreObjectRootProperty(storeObject);
return sharedProperty == null
- ? property.DeclaringEntityType.Model.GetIdentityIncrement()
+ ? property.DeclaringType.Model.GetIdentityIncrement()
: sharedProperty.GetIdentityIncrement(storeObject);
}
@@ -744,7 +744,7 @@ internal static SqlServerValueGenerationStrategy GetValueGenerationStrategy(
var annotation = property.FindAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy);
if (annotation?.Value != null
- && StoreObjectIdentifier.Create(property.DeclaringEntityType, storeObject.StoreObjectType) == storeObject)
+ && StoreObjectIdentifier.Create(property.DeclaringType, storeObject.StoreObjectType) == storeObject)
{
return (SqlServerValueGenerationStrategy)annotation.Value;
}
@@ -809,7 +809,7 @@ is StoreObjectIdentifier principal
private static SqlServerValueGenerationStrategy GetDefaultValueGenerationStrategy(IReadOnlyProperty property)
{
- var modelStrategy = property.DeclaringEntityType.Model.GetValueGenerationStrategy();
+ var modelStrategy = property.DeclaringType.Model.GetValueGenerationStrategy();
if (modelStrategy is SqlServerValueGenerationStrategy.SequenceHiLo or SqlServerValueGenerationStrategy.Sequence
&& IsCompatibleWithValueGeneration(property))
@@ -828,7 +828,7 @@ private static SqlServerValueGenerationStrategy GetDefaultValueGenerationStrateg
in StoreObjectIdentifier storeObject,
ITypeMappingSource? typeMappingSource)
{
- var modelStrategy = property.DeclaringEntityType.Model.GetValueGenerationStrategy();
+ var modelStrategy = property.DeclaringType.Model.GetValueGenerationStrategy();
if (modelStrategy is SqlServerValueGenerationStrategy.SequenceHiLo or SqlServerValueGenerationStrategy.Sequence
&& IsCompatibleWithValueGeneration(property, storeObject, typeMappingSource))
@@ -838,7 +838,7 @@ private static SqlServerValueGenerationStrategy GetDefaultValueGenerationStrateg
return modelStrategy == SqlServerValueGenerationStrategy.IdentityColumn
&& IsCompatibleWithValueGeneration(property, storeObject, typeMappingSource)
- ? property.DeclaringEntityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy
+ ? property.DeclaringType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy
? SqlServerValueGenerationStrategy.Sequence
: SqlServerValueGenerationStrategy.IdentityColumn
: SqlServerValueGenerationStrategy.None;
@@ -945,7 +945,7 @@ public static void SetValueGenerationStrategy(
{
throw new ArgumentException(
SqlServerStrings.IdentityBadType(
- property.Name, property.DeclaringEntityType.DisplayName(), propertyType.ShortDisplayName()));
+ property.Name, property.DeclaringType.DisplayName(), propertyType.ShortDisplayName()));
}
if (value is SqlServerValueGenerationStrategy.SequenceHiLo or SqlServerValueGenerationStrategy.Sequence
@@ -953,7 +953,7 @@ public static void SetValueGenerationStrategy(
{
throw new ArgumentException(
SqlServerStrings.SequenceBadType(
- property.Name, property.DeclaringEntityType.DisplayName(), propertyType.ShortDisplayName()));
+ property.Name, property.DeclaringType.DisplayName(), propertyType.ShortDisplayName()));
}
return value;
diff --git a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs
index 580ab776269..b6f8370227c 100644
--- a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs
+++ b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs
@@ -430,7 +430,7 @@ protected override void ValidateSharedColumnsCompatibility(
if (identityColumns.Count > 1)
{
var sb = new StringBuilder()
- .AppendJoin(identityColumns.Values.Select(p => "'" + p.DeclaringEntityType.DisplayName() + "." + p.Name + "'"));
+ .AppendJoin(identityColumns.Values.Select(p => "'" + p.DeclaringType.DisplayName() + "." + p.Name + "'"));
throw new InvalidOperationException(SqlServerStrings.MultipleIdentityColumns(sb, storeObject.DisplayName()));
}
}
@@ -464,9 +464,9 @@ protected override void ValidateCompatible(
{
throw new InvalidOperationException(
SqlServerStrings.DuplicateColumnNameValueGenerationStrategyMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
@@ -483,9 +483,9 @@ protected override void ValidateCompatible(
{
throw new InvalidOperationException(
SqlServerStrings.DuplicateColumnIdentityIncrementMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
@@ -497,9 +497,9 @@ protected override void ValidateCompatible(
{
throw new InvalidOperationException(
SqlServerStrings.DuplicateColumnIdentitySeedMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
@@ -512,9 +512,9 @@ protected override void ValidateCompatible(
{
throw new InvalidOperationException(
SqlServerStrings.DuplicateColumnSequenceMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
@@ -528,9 +528,9 @@ protected override void ValidateCompatible(
{
throw new InvalidOperationException(
SqlServerStrings.DuplicateColumnSparsenessMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs
index cb6cfc9761d..338cd8c1fd4 100644
--- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs
+++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs
@@ -97,11 +97,11 @@ public override void ProcessEntityTypeAnnotationChanged(
protected override ValueGenerated? GetValueGenerated(IConventionProperty property)
{
// TODO: move to relational?
- if (property.DeclaringEntityType.IsMappedToJson()
- && !property.DeclaringEntityType.FindOwnership()!.IsUnique
+ if (property.DeclaringType.IsMappedToJson()
#pragma warning disable EF1001 // Internal EF Core API usage.
- && property.IsOrdinalKeyProperty())
+ && property.IsOrdinalKeyProperty()
#pragma warning restore EF1001 // Internal EF Core API usage.
+ && (property.DeclaringType as IReadOnlyEntityType)?.FindOwnership()!.IsUnique == false)
{
return ValueGenerated.OnAdd;
}
@@ -141,8 +141,9 @@ public override void ProcessEntityTypeAnnotationChanged(
private static ValueGenerated? GetTemporalValueGenerated(IReadOnlyProperty property)
{
- var entityType = property.DeclaringEntityType;
- return entityType.IsTemporal()
+ var entityType = property.DeclaringType as IReadOnlyEntityType;
+ return entityType != null
+ && entityType.IsTemporal()
&& (entityType.GetPeriodStartPropertyName() == property.Name
|| entityType.GetPeriodEndPropertyName() == property.Name)
? ValueGenerated.OnAddOrUpdate
diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs
index 89d1f09b18f..2e58ef8dde1 100644
--- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs
+++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs
@@ -111,7 +111,7 @@ bool IsStrategyNoneNeeded(IReadOnlyProperty property, StoreObjectIdentifier stor
&& !property.TryGetDefaultValue(storeObject, out _)
&& property.GetDefaultValueSql(storeObject) == null
&& property.GetComputedColumnSql(storeObject) == null
- && property.DeclaringEntityType.Model.GetValueGenerationStrategy() == SqlServerValueGenerationStrategy.IdentityColumn)
+ && property.DeclaringType.Model.GetValueGenerationStrategy() == SqlServerValueGenerationStrategy.IdentityColumn)
{
var providerClrType = (property.GetValueConverter()
?? (property.FindRelationalTypeMapping(storeObject)
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs
index 911d23d5077..92d26598bce 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs
@@ -78,16 +78,16 @@ public static bool ShouldRetryOn(Exception? ex)
case 14355:
// SQL Error Code: 10936
// Resource ID : %d. The request limit for the elastic pool is %d and has been reached.
- // See 'http://go.microsoft.com/fwlink/?LinkId=267637' for assistance.
+ // See 'https://go.microsoft.com/fwlink/?LinkId=267637' for assistance.
case 10936:
// SQL Error Code: 10929
// Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d.
// However, the server is currently too busy to support requests greater than %d for this database.
- // For more information, see http://go.microsoft.com/fwlink/?LinkId=267637. Otherwise, please try again.
+ // For more information, see https://go.microsoft.com/fwlink/?LinkId=267637. Otherwise, please try again.
case 10929:
// SQL Error Code: 10928
// Resource ID: %d. The %s limit for the database is %d and has been reached. For more information,
- // see http://go.microsoft.com/fwlink/?LinkId=267637.
+ // see https://go.microsoft.com/fwlink/?LinkId=267637.
case 10928:
// SQL Error Code: 10922
// %ls failed. Rerun the statement.
diff --git a/src/EFCore.SqlServer/ValueGeneration/Internal/SqlServerValueGeneratorCache.cs b/src/EFCore.SqlServer/ValueGeneration/Internal/SqlServerValueGeneratorCache.cs
index 3312428503b..8278993c131 100644
--- a/src/EFCore.SqlServer/ValueGeneration/Internal/SqlServerValueGeneratorCache.cs
+++ b/src/EFCore.SqlServer/ValueGeneration/Internal/SqlServerValueGeneratorCache.cs
@@ -34,7 +34,7 @@ public virtual SqlServerSequenceValueGeneratorState GetOrAddSequenceState(
IProperty property,
IRelationalConnection connection)
{
- var tableIdentifier = StoreObjectIdentifier.Create(property.DeclaringEntityType, StoreObjectType.Table);
+ var tableIdentifier = StoreObjectIdentifier.Create(property.DeclaringType, StoreObjectType.Table);
var sequence = tableIdentifier != null
? property.FindHiLoSequence(tableIdentifier.Value)
: property.FindHiLoSequence();
diff --git a/src/EFCore.SqlServer/ValueGeneration/Internal/SqlServerValueGeneratorSelector.cs b/src/EFCore.SqlServer/ValueGeneration/Internal/SqlServerValueGeneratorSelector.cs
index 5a8d83676d6..05acf3560ca 100644
--- a/src/EFCore.SqlServer/ValueGeneration/Internal/SqlServerValueGeneratorSelector.cs
+++ b/src/EFCore.SqlServer/ValueGeneration/Internal/SqlServerValueGeneratorSelector.cs
@@ -53,12 +53,12 @@ public SqlServerValueGeneratorSelector(
/// 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 override ValueGenerator Select(IProperty property, IEntityType entityType)
+ public override ValueGenerator Select(IProperty property, ITypeBase typeBase)
{
if (property.GetValueGeneratorFactory() != null
|| property.GetValueGenerationStrategy() != SqlServerValueGenerationStrategy.SequenceHiLo)
{
- return base.Select(property, entityType);
+ return base.Select(property, typeBase);
}
var propertyType = property.ClrType.UnwrapNullableType().UnwrapEnumType();
@@ -96,7 +96,7 @@ public override ValueGenerator Select(IProperty property, IEntityType entityType
throw new ArgumentException(
CoreStrings.InvalidValueGeneratorFactoryProperty(
- nameof(SqlServerSequenceValueGeneratorFactory), property.Name, property.DeclaringEntityType.DisplayName()));
+ nameof(SqlServerSequenceValueGeneratorFactory), property.Name, property.DeclaringType.DisplayName()));
}
///
@@ -105,10 +105,10 @@ public override ValueGenerator Select(IProperty property, IEntityType entityType
/// 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.
///
- protected override ValueGenerator? FindForType(IProperty property, IEntityType entityType, Type clrType)
+ protected override ValueGenerator? FindForType(IProperty property, ITypeBase typeBase, Type clrType)
=> property.ClrType.UnwrapNullableType() == typeof(Guid)
? property.ValueGenerated == ValueGenerated.Never || property.GetDefaultValueSql() != null
? new TemporaryGuidValueGenerator()
: new SequentialGuidValueGenerator()
- : base.FindForType(property, entityType, clrType);
+ : base.FindForType(property, typeBase, clrType);
}
diff --git a/src/EFCore.Sqlite.Core/Infrastructure/Internal/SqliteModelValidator.cs b/src/EFCore.Sqlite.Core/Infrastructure/Internal/SqliteModelValidator.cs
index e2c12d98d82..d601b461a1f 100644
--- a/src/EFCore.Sqlite.Core/Infrastructure/Internal/SqliteModelValidator.cs
+++ b/src/EFCore.Sqlite.Core/Infrastructure/Internal/SqliteModelValidator.cs
@@ -115,9 +115,9 @@ protected override void ValidateCompatible(
{
throw new InvalidOperationException(
SqliteStrings.DuplicateColumnNameSridMismatch(
- duplicateProperty.DeclaringEntityType.DisplayName(),
+ duplicateProperty.DeclaringType.DisplayName(),
duplicateProperty.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
columnName,
storeObject.DisplayName()));
diff --git a/src/EFCore.Sqlite.Core/Metadata/Internal/SqliteAnnotationProvider.cs b/src/EFCore.Sqlite.Core/Metadata/Internal/SqliteAnnotationProvider.cs
index fdb63ec6830..8aec3df3a1c 100644
--- a/src/EFCore.Sqlite.Core/Metadata/Internal/SqliteAnnotationProvider.cs
+++ b/src/EFCore.Sqlite.Core/Metadata/Internal/SqliteAnnotationProvider.cs
@@ -67,7 +67,7 @@ public override IEnumerable For(IColumn column, bool designTime)
// Model validation ensures that these facets are the same on all mapped properties
var property = column.PropertyMappings.First().Property;
// Only return auto increment for integer single column primary key
- var primaryKey = property.DeclaringEntityType.FindPrimaryKey();
+ var primaryKey = property.DeclaringType.FundamentalEntityType.FindPrimaryKey();
if (primaryKey is { Properties.Count: 1 }
&& primaryKey.Properties[0] == property
&& property.ValueGenerated == ValueGenerated.OnAdd
diff --git a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs
index 8794b7b9b1b..7d8d5beba71 100644
--- a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs
+++ b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs
@@ -60,7 +60,7 @@ public static string IncompatibleSqlReturningClauseMismatch(object? table, objec
table, entityType, otherEntityType, entityTypeWithSqlReturningClause, entityTypeWithoutSqlReturningClause);
///
- /// SQLite does not support this migration operation ('{operation}'). See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ /// SQLite does not support this migration operation ('{operation}'). See https://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
///
public static string InvalidMigrationOperation(object? operation)
=> string.Format(
@@ -68,7 +68,7 @@ public static string InvalidMigrationOperation(object? operation)
operation);
///
- /// Generating idempotent scripts for migrations is not currently supported for SQLite. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ /// Generating idempotent scripts for migrations is not currently supported for SQLite. See https://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
///
public static string MigrationScriptGenerationNotSupported
=> GetString("MigrationScriptGenerationNotSupported");
@@ -82,13 +82,13 @@ public static string OrderByNotSupported(object? type)
type);
///
- /// SQLite does not support sequences. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ /// SQLite does not support sequences. See https://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
///
public static string SequencesNotSupported
=> GetString("SequencesNotSupported");
///
- /// SQLite does not support stored procedures, but one has been configured on entity type '{entityType}'. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ /// SQLite does not support stored procedures, but one has been configured on entity type '{entityType}'. See https://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
///
public static string StoredProceduresNotSupported(object? entityType)
=> string.Format(
diff --git a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
index aa3444f088b..bf8895f442b 100644
--- a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
+++ b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
@@ -133,7 +133,7 @@
Cannot use table '{table}' for entity type '{entityType}' since it is being used for entity type '{otherEntityType}' and entity type '{entityTypeWithSqlReturningClause}' is configured to use the SQL RETURNING clause, but entity type '{entityTypeWithoutSqlReturningClause}' is not.
- SQLite does not support this migration operation ('{operation}'). See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ SQLite does not support this migration operation ('{operation}'). See https://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
The entity type '{entityType}' has composite key '{key}' which is configured to use generated values. SQLite does not support generated values on composite keys.
@@ -208,15 +208,15 @@
Warning SqliteEventId.SchemasNotSupportedWarning
- Generating idempotent scripts for migrations is not currently supported for SQLite. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ Generating idempotent scripts for migrations is not currently supported for SQLite. See https://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
SQLite does not support expressions of type '{type}' in ORDER BY clauses. Convert the values to a supported type, or use LINQ to Objects to order the results on the client side.
- SQLite does not support sequences. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ SQLite does not support sequences. See https://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
- SQLite does not support stored procedures, but one has been configured on entity type '{entityType}'. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ SQLite does not support stored procedures, but one has been configured on entity type '{entityType}'. See https://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
\ No newline at end of file
diff --git a/src/EFCore/ChangeTracking/EntityEntry`.cs b/src/EFCore/ChangeTracking/EntityEntry`.cs
index 050be2e5439..8901fd76d78 100644
--- a/src/EFCore/ChangeTracking/EntityEntry`.cs
+++ b/src/EFCore/ChangeTracking/EntityEntry`.cs
@@ -237,7 +237,7 @@ private static void ValidateType(IProperty? property)
throw new ArgumentException(
CoreStrings.WrongGenericPropertyType(
property.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.ClrType.ShortDisplayName(),
typeof(TProperty).ShortDisplayName()));
}
diff --git a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs
index 56cfd4917bd..c8c54fcc2d6 100644
--- a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs
+++ b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs
@@ -165,7 +165,7 @@ private void SetValue(int index, object? value)
throw new InvalidCastException(
CoreStrings.InvalidType(
property.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
value.GetType().DisplayName(),
property.ClrType.DisplayName()));
}
@@ -177,7 +177,7 @@ private void SetValue(int index, object? value)
throw new InvalidOperationException(
CoreStrings.ValueCannotBeNull(
property.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.ClrType.DisplayName()));
}
}
diff --git a/src/EFCore/ChangeTracking/Internal/EmptyShadowValuesFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/EmptyShadowValuesFactoryFactory.cs
index 4aabe0eac85..d5b61954ce0 100644
--- a/src/EFCore/ChangeTracking/Internal/EmptyShadowValuesFactoryFactory.cs
+++ b/src/EFCore/ChangeTracking/Internal/EmptyShadowValuesFactoryFactory.cs
@@ -28,8 +28,8 @@ protected override int GetPropertyIndex(IPropertyBase propertyBase)
/// 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.
///
- protected override int GetPropertyCount(IEntityType entityType)
- => entityType.ShadowPropertyCount();
+ protected override int GetPropertyCount(IRuntimeTypeBase typeBase)
+ => typeBase.ShadowPropertyCount;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
index dbd8d4f3d04..568df9bde78 100644
--- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
+++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
@@ -19,7 +19,6 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
///
public sealed partial class InternalEntityEntry : IUpdateEntry
{
- // ReSharper disable once FieldCanBeMadeReadOnly.Local
private readonly StateData _stateData;
private OriginalValues _originalValues;
private RelationshipsSnapshot _relationshipsSnapshot;
@@ -39,10 +38,10 @@ public InternalEntityEntry(
object entity)
{
StateManager = stateManager;
- EntityType = entityType;
+ EntityType = (IRuntimeEntityType)entityType;
Entity = entity;
- _shadowValues = entityType.GetEmptyShadowValuesFactory()();
- _stateData = new StateData(entityType.PropertyCount(), entityType.NavigationCount());
+ _shadowValues = EntityType.EmptyShadowValuesFactory();
+ _stateData = new StateData(EntityType.PropertyCount, EntityType.NavigationCount);
MarkShadowPropertiesNotSet(entityType);
}
@@ -60,10 +59,10 @@ public InternalEntityEntry(
in ValueBuffer valueBuffer)
{
StateManager = stateManager;
- EntityType = entityType;
+ EntityType = (IRuntimeEntityType)entityType;
Entity = entity;
- _shadowValues = ((IRuntimeEntityType)entityType).ShadowValuesFactory(valueBuffer);
- _stateData = new StateData(entityType.PropertyCount(), entityType.NavigationCount());
+ _shadowValues = EntityType.ShadowValuesFactory(valueBuffer);
+ _stateData = new StateData(EntityType.PropertyCount, EntityType.NavigationCount);
}
///
@@ -107,7 +106,7 @@ void IUpdateEntry.SetPropertyModified(IProperty property)
/// 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 IEntityType EntityType { [DebuggerStepThrough] get; }
+ public IRuntimeEntityType EntityType { get; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -271,7 +270,7 @@ private bool PrepareForAdd(EntityState newState)
if (EntityState == EntityState.Modified)
{
_stateData.FlagAllProperties(
- EntityType.PropertyCount(), PropertyFlag.Modified,
+ EntityType.PropertyCount, PropertyFlag.Modified,
flagged: false);
}
@@ -309,7 +308,7 @@ private void SetEntityState(EntityState oldState, EntityState newState, bool acc
if (newState == EntityState.Modified
&& modifyProperties)
{
- _stateData.FlagAllProperties(entityType.PropertyCount(), PropertyFlag.Modified, flagged: true);
+ _stateData.FlagAllProperties(EntityType.PropertyCount, PropertyFlag.Modified, flagged: true);
// Hot path; do not use LINQ
foreach (var property in entityType.GetProperties())
@@ -329,7 +328,7 @@ private void SetEntityState(EntityState oldState, EntityState newState, bool acc
if (newState == EntityState.Unchanged)
{
_stateData.FlagAllProperties(
- entityType.PropertyCount(), PropertyFlag.Modified,
+ EntityType.PropertyCount, PropertyFlag.Modified,
flagged: false);
}
@@ -371,7 +370,7 @@ private void SetEntityState(EntityState oldState, EntityState newState, bool acc
if (newState is EntityState.Deleted or EntityState.Detached
&& HasConceptualNull)
{
- _stateData.FlagAllProperties(entityType.PropertyCount(), PropertyFlag.Null, flagged: false);
+ _stateData.FlagAllProperties(EntityType.PropertyCount, PropertyFlag.Null, flagged: false);
}
if (oldState is EntityState.Detached or EntityState.Unchanged)
@@ -1464,9 +1463,9 @@ public void AcceptChanges()
_temporaryValues = new SidecarValues();
}
- _stateData.FlagAllProperties(EntityType.PropertyCount(), PropertyFlag.IsStoreGenerated, false);
- _stateData.FlagAllProperties(EntityType.PropertyCount(), PropertyFlag.IsTemporary, false);
- _stateData.FlagAllProperties(EntityType.PropertyCount(), PropertyFlag.Unknown, false);
+ _stateData.FlagAllProperties(EntityType.PropertyCount, PropertyFlag.IsStoreGenerated, false);
+ _stateData.FlagAllProperties(EntityType.PropertyCount, PropertyFlag.IsTemporary, false);
+ _stateData.FlagAllProperties(EntityType.PropertyCount, PropertyFlag.Unknown, false);
var currentState = EntityState;
switch (currentState)
@@ -1684,7 +1683,7 @@ public void DiscardStoreGeneratedValues()
if (!_storeGeneratedValues.IsEmpty)
{
_storeGeneratedValues = new SidecarValues();
- _stateData.FlagAllProperties(EntityType.PropertyCount(), PropertyFlag.IsStoreGenerated, false);
+ _stateData.FlagAllProperties(EntityType.PropertyCount, PropertyFlag.IsStoreGenerated, false);
}
}
@@ -1991,6 +1990,9 @@ public DebugView DebugView
IUpdateEntry? IUpdateEntry.SharedIdentityEntry
=> SharedIdentityEntry;
+ IEntityType IUpdateEntry.EntityType
+ => EntityType;
+
private enum CurrentValueType
{
Normal,
diff --git a/src/EFCore/ChangeTracking/Internal/KeyPropagator.cs b/src/EFCore/ChangeTracking/Internal/KeyPropagator.cs
index 5069c7bc6e5..e4df407b783 100644
--- a/src/EFCore/ChangeTracking/Internal/KeyPropagator.cs
+++ b/src/EFCore/ChangeTracking/Internal/KeyPropagator.cs
@@ -37,7 +37,7 @@ public KeyPropagator(
{
Check.DebugAssert(property.IsForeignKey(), $"property {property} is not part of an FK");
- var generationProperty = property.FindGenerationProperty();
+ var generationProperty = (IProperty?)property.FindGenerationProperty();
var principalEntry = TryPropagateValue(entry, property, generationProperty);
if (principalEntry == null
@@ -48,7 +48,7 @@ public KeyPropagator(
generationProperty,
generationProperty == property
? entry.EntityType
- : generationProperty?.DeclaringEntityType);
+ : generationProperty?.DeclaringType);
if (valueGenerator != null)
{
@@ -72,7 +72,7 @@ public KeyPropagator(
{
Check.DebugAssert(property.IsForeignKey(), $"property {property} is not part of an FK");
- var generationProperty = property.FindGenerationProperty();
+ var generationProperty = (IProperty?)property.FindGenerationProperty();
var principalEntry = TryPropagateValue(entry, property, generationProperty);
if (principalEntry == null
@@ -83,7 +83,7 @@ public KeyPropagator(
generationProperty,
generationProperty == property
? entry.EntityType
- : generationProperty?.DeclaringEntityType);
+ : generationProperty?.DeclaringType);
if (valueGenerator != null)
{
@@ -166,8 +166,8 @@ private static void SetValue(InternalEntityEntry entry, IProperty property, Valu
return null;
}
- private ValueGenerator? TryGetValueGenerator(IProperty? generationProperty, IEntityType? entityType)
+ private ValueGenerator? TryGetValueGenerator(IProperty? generationProperty, ITypeBase? typeBase)
=> generationProperty != null
- ? _valueGeneratorSelector.Select(generationProperty, entityType!)
+ ? _valueGeneratorSelector.Select(generationProperty, typeBase!)
: null;
}
diff --git a/src/EFCore/ChangeTracking/Internal/OriginalValues.cs b/src/EFCore/ChangeTracking/Internal/OriginalValues.cs
index fe5a99a54b9..b25b6c49bd1 100644
--- a/src/EFCore/ChangeTracking/Internal/OriginalValues.cs
+++ b/src/EFCore/ChangeTracking/Internal/OriginalValues.cs
@@ -22,7 +22,7 @@ public OriginalValues(InternalEntityEntry entry)
if (index == -1)
{
throw new InvalidOperationException(
- CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringEntityType.DisplayName()));
+ CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringType.DisplayName()));
}
return IsEmpty ? entry[property] : _values[index];
@@ -33,7 +33,7 @@ public T GetValue(InternalEntityEntry entry, IProperty property, int index)
if (index == -1)
{
throw new InvalidOperationException(
- CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringEntityType.DisplayName()));
+ CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringType.DisplayName()));
}
return IsEmpty ? entry.GetCurrentValue(property) : _values.GetValue(index);
@@ -50,7 +50,7 @@ public void SetValue(IProperty property, object? value, int index)
if (index == -1)
{
throw new InvalidOperationException(
- CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringEntityType.DisplayName()));
+ CoreStrings.OriginalValueNotTracked(property.Name, property.DeclaringType.DisplayName()));
}
}
@@ -59,7 +59,7 @@ public void SetValue(IProperty property, object? value, int index)
{
throw new InvalidOperationException(
CoreStrings.ValueCannotBeNull(
- property.Name, property.DeclaringEntityType.DisplayName(), property.ClrType.DisplayName()));
+ property.Name, property.DeclaringType.DisplayName(), property.ClrType.DisplayName()));
}
_values[index] = SnapshotValue(property, value);
diff --git a/src/EFCore/ChangeTracking/Internal/OriginalValuesFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/OriginalValuesFactoryFactory.cs
index bb617e971c2..d2fa8816177 100644
--- a/src/EFCore/ChangeTracking/Internal/OriginalValuesFactoryFactory.cs
+++ b/src/EFCore/ChangeTracking/Internal/OriginalValuesFactoryFactory.cs
@@ -28,8 +28,8 @@ protected override int GetPropertyIndex(IPropertyBase propertyBase)
/// 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.
///
- protected override int GetPropertyCount(IEntityType entityType)
- => entityType.OriginalValueCount();
+ protected override int GetPropertyCount(IRuntimeTypeBase typeBase)
+ => typeBase.OriginalValueCount;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/ChangeTracking/Internal/RelationshipSnapshotFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/RelationshipSnapshotFactoryFactory.cs
index 72bcd6319a4..8c4bb0e69f8 100644
--- a/src/EFCore/ChangeTracking/Internal/RelationshipSnapshotFactoryFactory.cs
+++ b/src/EFCore/ChangeTracking/Internal/RelationshipSnapshotFactoryFactory.cs
@@ -28,8 +28,8 @@ protected override int GetPropertyIndex(IPropertyBase propertyBase)
/// 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.
///
- protected override int GetPropertyCount(IEntityType entityType)
- => entityType.RelationshipPropertyCount();
+ protected override int GetPropertyCount(IRuntimeTypeBase typeBase)
+ => typeBase.RelationshipPropertyCount;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/ChangeTracking/Internal/ShadowValuesFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/ShadowValuesFactoryFactory.cs
index cf6b4ee81d9..02a03f14ae4 100644
--- a/src/EFCore/ChangeTracking/Internal/ShadowValuesFactoryFactory.cs
+++ b/src/EFCore/ChangeTracking/Internal/ShadowValuesFactoryFactory.cs
@@ -28,8 +28,8 @@ protected override int GetPropertyIndex(IPropertyBase propertyBase)
/// 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.
///
- protected override int GetPropertyCount(IEntityType entityType)
- => entityType.ShadowPropertyCount();
+ protected override int GetPropertyCount(IRuntimeTypeBase typeBase)
+ => typeBase.ShadowPropertyCount;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/ChangeTracking/Internal/SidecarValues.cs b/src/EFCore/ChangeTracking/Internal/SidecarValues.cs
index 6c2cfc47676..88d531092ca 100644
--- a/src/EFCore/ChangeTracking/Internal/SidecarValues.cs
+++ b/src/EFCore/ChangeTracking/Internal/SidecarValues.cs
@@ -41,7 +41,7 @@ public void SetValue(IProperty property, object? value, int index)
{
throw new InvalidOperationException(
CoreStrings.ValueCannotBeNull(
- property.Name, property.DeclaringEntityType.DisplayName(), property.ClrType.DisplayName()));
+ property.Name, property.DeclaringType.DisplayName(), property.ClrType.DisplayName()));
}
_values[index] = SnapshotValue(property, value);
diff --git a/src/EFCore/ChangeTracking/Internal/SidecarValuesFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/SidecarValuesFactoryFactory.cs
index d8030894f67..40fcd76ae1b 100644
--- a/src/EFCore/ChangeTracking/Internal/SidecarValuesFactoryFactory.cs
+++ b/src/EFCore/ChangeTracking/Internal/SidecarValuesFactoryFactory.cs
@@ -28,8 +28,8 @@ protected override int GetPropertyIndex(IPropertyBase propertyBase)
/// 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.
///
- protected override int GetPropertyCount(IEntityType entityType)
- => entityType.StoreGeneratedCount();
+ protected override int GetPropertyCount(IRuntimeTypeBase typeBase)
+ => typeBase.StoreGeneratedCount;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs
index 04499ea5858..c69e316928a 100644
--- a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs
+++ b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs
@@ -21,11 +21,11 @@ public abstract class SnapshotFactoryFactory
/// 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 Func CreateEmpty(IEntityType entityType)
- => GetPropertyCount(entityType) == 0
+ public virtual Func CreateEmpty(IRuntimeTypeBase typeBase)
+ => GetPropertyCount(typeBase) == 0
? (() => Snapshot.Empty)
: Expression.Lambda>(
- CreateConstructorExpression(entityType, null!))
+ CreateConstructorExpression(typeBase, null!))
.Compile();
///
@@ -35,15 +35,15 @@ public virtual Func CreateEmpty(IEntityType entityType)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected virtual Expression CreateConstructorExpression(
- IEntityType entityType,
+ IRuntimeTypeBase typeBase,
ParameterExpression? parameter)
{
- var count = GetPropertyCount(entityType);
+ var count = GetPropertyCount(typeBase);
var types = new Type[count];
var propertyBases = new IPropertyBase?[count];
- foreach (var propertyBase in entityType.GetPropertiesAndNavigations())
+ foreach (var propertyBase in typeBase.GetSnapshottableMembers())
{
var index = GetPropertyIndex(propertyBase);
if (index >= 0)
@@ -62,7 +62,7 @@ protected virtual Expression CreateConstructorExpression(
{
snapshotExpressions.Add(
CreateSnapshotExpression(
- entityType.ClrType,
+ typeBase.ClrType,
parameter,
types.Skip(i).Take(Snapshot.MaxGenericTypes).ToArray(),
propertyBases.Skip(i).Take(Snapshot.MaxGenericTypes).ToList()));
@@ -77,7 +77,7 @@ protected virtual Expression CreateConstructorExpression(
}
else
{
- constructorExpression = CreateSnapshotExpression(entityType.ClrType, parameter, types, propertyBases);
+ constructorExpression = CreateSnapshotExpression(typeBase.ClrType, parameter, types, propertyBases);
}
return constructorExpression;
@@ -257,7 +257,7 @@ protected virtual Expression CreateReadValueExpression(
/// 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.
///
- protected abstract int GetPropertyCount(IEntityType entityType);
+ protected abstract int GetPropertyCount(IRuntimeTypeBase typeBase);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory`.cs b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory`.cs
index 756a5a70d31..66f8fc1c536 100644
--- a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory`.cs
+++ b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory`.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+
namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
///
@@ -17,9 +19,9 @@ public abstract class SnapshotFactoryFactory : SnapshotFactoryFactory
/// 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 Func Create(IEntityType entityType)
+ public virtual Func Create(IRuntimeTypeBase typeBase)
{
- if (GetPropertyCount(entityType) == 0)
+ if (GetPropertyCount(typeBase) == 0)
{
return _ => Snapshot.Empty;
}
@@ -27,7 +29,7 @@ public virtual Func Create(IEntityType entityType)
var parameter = Expression.Parameter(typeof(TInput), "source");
return Expression.Lambda>(
- CreateConstructorExpression(entityType, parameter),
+ CreateConstructorExpression(typeBase, parameter),
parameter)
.Compile();
}
diff --git a/src/EFCore/ChangeTracking/Internal/StateManager.cs b/src/EFCore/ChangeTracking/Internal/StateManager.cs
index c6cb87aab01..e98815ec490 100644
--- a/src/EFCore/ChangeTracking/Internal/StateManager.cs
+++ b/src/EFCore/ChangeTracking/Internal/StateManager.cs
@@ -275,8 +275,9 @@ public virtual InternalEntityEntry GetOrCreateEntry(object entity, IEntityType?
public virtual InternalEntityEntry CreateEntry(IDictionary values, IEntityType entityType)
{
var i = 0;
- var valuesArray = new object?[entityType.PropertyCount()];
- var shadowPropertyValuesArray = new object?[entityType.ShadowPropertyCount()];
+ var runtimeEntityType = (IRuntimeEntityType)entityType;
+ var valuesArray = new object?[runtimeEntityType.PropertyCount];
+ var shadowPropertyValuesArray = new object?[runtimeEntityType.ShadowPropertyCount];
foreach (var property in entityType.GetProperties())
{
valuesArray[i++] = values.TryGetValue(property.Name, out var value)
diff --git a/src/EFCore/ChangeTracking/Internal/ValueGenerationManager.cs b/src/EFCore/ChangeTracking/Internal/ValueGenerationManager.cs
index 2cb4d4a577e..1686a1f695f 100644
--- a/src/EFCore/ChangeTracking/Internal/ValueGenerationManager.cs
+++ b/src/EFCore/ChangeTracking/Internal/ValueGenerationManager.cs
@@ -189,7 +189,7 @@ public virtual async Task GenerateAsync(
}
private ValueGenerator GetValueGenerator(IProperty property)
- => _valueGeneratorSelector.Select(property, property.DeclaringEntityType);
+ => _valueGeneratorSelector.Select(property, property.DeclaringType);
private static void SetGeneratedValue(InternalEntityEntry entry, IProperty property, object? generatedValue, bool isTemporary)
{
diff --git a/src/EFCore/ChangeTracking/LocalView.cs b/src/EFCore/ChangeTracking/LocalView.cs
index 6ab659f170a..03c56b2cc17 100644
--- a/src/EFCore/ChangeTracking/LocalView.cs
+++ b/src/EFCore/ChangeTracking/LocalView.cs
@@ -845,7 +845,7 @@ private IProperty FindAndValidateProperty(string propertyName)
throw new ArgumentException(
CoreStrings.WrongGenericPropertyType(
property.Name,
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.ClrType.ShortDisplayName(),
typeof(TProperty).ShortDisplayName()));
}
diff --git a/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGenerator.cs
index 7f97dffbe13..152e28af957 100644
--- a/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGenerator.cs
+++ b/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGenerator.cs
@@ -68,6 +68,44 @@ public virtual void Generate(IEntityType entityType, CSharpRuntimeAnnotationCode
GenerateSimpleAnnotations(parameters);
}
+ ///
+ public virtual void Generate(IComplexProperty complexProperty, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
+ {
+ var annotations = parameters.Annotations;
+ if (!parameters.IsRuntime)
+ {
+ foreach (var (key, _) in annotations)
+ {
+ if (CoreAnnotationNames.AllNames.Contains(key)
+ && key != CoreAnnotationNames.DiscriminatorMappingComplete)
+ {
+ annotations.Remove(key);
+ }
+ }
+ }
+
+ GenerateSimpleAnnotations(parameters);
+ }
+
+ ///
+ public virtual void Generate(IComplexType complexType, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
+ {
+ var annotations = parameters.Annotations;
+ if (!parameters.IsRuntime)
+ {
+ foreach (var (key, _) in annotations)
+ {
+ if (CoreAnnotationNames.AllNames.Contains(key)
+ && key != CoreAnnotationNames.DiscriminatorMappingComplete)
+ {
+ annotations.Remove(key);
+ }
+ }
+ }
+
+ GenerateSimpleAnnotations(parameters);
+ }
+
///
public virtual void Generate(IProperty property, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
{
diff --git a/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGeneratorParameters.cs b/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGeneratorParameters.cs
index a13bd7f5f25..3cbce4374ec 100644
--- a/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGeneratorParameters.cs
+++ b/src/EFCore/Design/Internal/CSharpRuntimeAnnotationCodeGeneratorParameters.cs
@@ -82,8 +82,8 @@ public CSharpRuntimeAnnotationCodeGeneratorParameters(
public bool IsRuntime { get; init; }
///
- /// Gets or sets a value indicating whther nullable reference types are enabled.
+ /// Gets or sets a value indicating whether nullable reference types are enabled.
///
- /// A value indicating whther nullable reference types are enabled.
+ /// A value indicating whether nullable reference types are enabled.
public bool UseNullableReferenceTypes { get; init; }
}
diff --git a/src/EFCore/Design/Internal/ICSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore/Design/Internal/ICSharpRuntimeAnnotationCodeGenerator.cs
index 6d960da09f7..f5a8145037f 100644
--- a/src/EFCore/Design/Internal/ICSharpRuntimeAnnotationCodeGenerator.cs
+++ b/src/EFCore/Design/Internal/ICSharpRuntimeAnnotationCodeGenerator.cs
@@ -27,6 +27,20 @@ public interface ICSharpRuntimeAnnotationCodeGenerator
/// Additional parameters used during code generation.
void Generate(IEntityType entityType, CSharpRuntimeAnnotationCodeGeneratorParameters parameters);
+ ///
+ /// Generates code to create the given annotations.
+ ///
+ /// The entity type to which the annotations are applied.
+ /// Additional parameters used during code generation.
+ void Generate(IComplexProperty complexProperty, CSharpRuntimeAnnotationCodeGeneratorParameters parameters);
+
+ ///
+ /// Generates code to create the given annotations.
+ ///
+ /// The entity type to which the annotations are applied.
+ /// Additional parameters used during code generation.
+ void Generate(IComplexType complexType, CSharpRuntimeAnnotationCodeGeneratorParameters parameters);
+
///
/// Generates code to create the given annotations.
///
diff --git a/src/EFCore/Diagnostics/ComplexPropertyEventData.cs b/src/EFCore/Diagnostics/ComplexPropertyEventData.cs
new file mode 100644
index 00000000000..c38393b9860
--- /dev/null
+++ b/src/EFCore/Diagnostics/ComplexPropertyEventData.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Diagnostics;
+
+///
+/// A event payload class for events that have
+/// a property.
+///
+///
+/// See Logging, events, and diagnostics for more information and examples.
+///
+public class ComplexPropertyEventData : EventData
+{
+ ///
+ /// Constructs the event payload.
+ ///
+ /// The event definition.
+ /// A delegate that generates a log message for this event.
+ /// The property.
+ public ComplexPropertyEventData(
+ EventDefinitionBase eventDefinition,
+ Func messageGenerator,
+ IReadOnlyComplexProperty property)
+ : base(eventDefinition, messageGenerator)
+ {
+ Property = property;
+ }
+
+ ///
+ /// The property.
+ ///
+ public virtual IReadOnlyComplexProperty Property { get; }
+}
diff --git a/src/EFCore/Diagnostics/CoreEventId.cs b/src/EFCore/Diagnostics/CoreEventId.cs
index 2d4415d1c1a..edef6150287 100644
--- a/src/EFCore/Diagnostics/CoreEventId.cs
+++ b/src/EFCore/Diagnostics/CoreEventId.cs
@@ -120,6 +120,7 @@ private enum Id
MappedEntityTypeIgnoredWarning,
MappedNavigationIgnoredWarning,
MappedPropertyIgnoredWarning,
+ MappedComplexPropertyIgnoredWarning,
// ChangeTracking events
DetectChangesStarting = CoreBaseId + 800,
@@ -566,6 +567,23 @@ private static EventId MakeModelValidationId(Id id)
///
public static readonly EventId MappedPropertyIgnoredWarning = MakeModelId(Id.MappedPropertyIgnoredWarning);
+ ///
+ /// A property was first mapped explicitly and then ignored.
+ ///
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ /// See Modeling entity types and relationships for more information and
+ /// examples.
+ ///
+ ///
+ public static readonly EventId MappedComplexPropertyIgnoredWarning = MakeModelId(Id.MappedComplexPropertyIgnoredWarning);
+
///
/// An index was not created as the properties are already covered.
///
diff --git a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs
index 59cbf53d00b..ba669747919 100644
--- a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs
+++ b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs
@@ -1261,7 +1261,7 @@ public static void ShadowForeignKeyPropertyCreated(
if (diagnostics.ShouldLog(definition))
{
- definition.Log(diagnostics, property.DeclaringEntityType.DisplayName(), property.Name, basePropertyName);
+ definition.Log(diagnostics, property.DeclaringType.DisplayName(), property.Name, basePropertyName);
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -1280,7 +1280,7 @@ private static string ShadowForeignKeyPropertyCreated(EventDefinitionBase defini
{
var d = (EventDefinition)definition;
var p = (UniquifiedPropertyEventData)payload;
- return d.GenerateMessage(p.Property.DeclaringEntityType.DisplayName(), p.Property.Name, p.BasePropertyName);
+ return d.GenerateMessage(p.Property.DeclaringType.DisplayName(), p.Property.Name, p.BasePropertyName);
}
///
@@ -1296,7 +1296,7 @@ public static void ShadowPropertyCreated(
if (diagnostics.ShouldLog(definition))
{
- definition.Log(diagnostics, property.DeclaringEntityType.DisplayName(), property.Name);
+ definition.Log(diagnostics, property.DeclaringType.DisplayName(), property.Name);
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -1314,7 +1314,7 @@ private static string ShadowPropertyCreated(EventDefinitionBase definition, Even
{
var d = (EventDefinition)definition;
var p = (PropertyEventData)payload;
- return d.GenerateMessage(p.Property.DeclaringEntityType.DisplayName(), p.Property.Name);
+ return d.GenerateMessage(p.Property.DeclaringType.DisplayName(), p.Property.Name);
}
///
@@ -1330,7 +1330,7 @@ public static void CollectionWithoutComparer(
if (diagnostics.ShouldLog(definition))
{
- definition.Log(diagnostics, property.DeclaringEntityType.DisplayName(), property.Name);
+ definition.Log(diagnostics, property.DeclaringType.DisplayName(), property.Name);
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -1348,7 +1348,7 @@ private static string CollectionWithoutComparer(EventDefinitionBase definition,
{
var d = (EventDefinition)definition;
var p = (PropertyEventData)payload;
- return d.GenerateMessage(p.Property.DeclaringEntityType.DisplayName(), p.Property.Name);
+ return d.GenerateMessage(p.Property.DeclaringType.DisplayName(), p.Property.Name);
}
///
@@ -1660,7 +1660,7 @@ public static void MultiplePrimaryKeyCandidates(
diagnostics,
firstProperty.Name,
secondProperty.Name,
- firstProperty.DeclaringEntityType.DisplayName());
+ firstProperty.DeclaringType.DisplayName());
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -2092,7 +2092,7 @@ public static void PropertyChangeDetected(
if (diagnostics.ShouldLog(definition))
{
- definition.Log(diagnostics, property.DeclaringEntityType.ShortName(), property.Name);
+ definition.Log(diagnostics, property.DeclaringType.ShortName(), property.Name);
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -2114,7 +2114,7 @@ private static string PropertyChangeDetected(EventDefinitionBase definition, Eve
var d = (EventDefinition)definition;
var p = (PropertyChangedEventData)payload;
return d.GenerateMessage(
- p.Property.DeclaringEntityType.ShortName(),
+ p.Property.DeclaringType.ShortName(),
p.Property.Name);
}
@@ -2139,11 +2139,11 @@ public static void PropertyChangeDetectedSensitive(
{
definition.Log(
diagnostics,
- property.DeclaringEntityType.ShortName(),
+ property.DeclaringType.ShortName(),
property.Name,
oldValue,
newValue,
- internalEntityEntry.BuildCurrentValuesString(property.DeclaringEntityType.FindPrimaryKey()!.Properties));
+ internalEntityEntry.BuildCurrentValuesString(((IEntityType)property.DeclaringType).FindPrimaryKey()!.Properties));
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -2165,11 +2165,12 @@ private static string PropertyChangeDetectedSensitive(EventDefinitionBase defini
var d = (EventDefinition)definition;
var p = (PropertyChangedEventData)payload;
return d.GenerateMessage(
- p.Property.DeclaringEntityType.ShortName(),
+ p.Property.DeclaringType.ShortName(),
p.Property.Name,
p.OldValue,
p.NewValue,
- p.EntityEntry.GetInfrastructure().BuildCurrentValuesString(p.Property.DeclaringEntityType.FindPrimaryKey()!.Properties));
+ p.EntityEntry.GetInfrastructure().BuildCurrentValuesString(
+ ((IEntityType)p.Property.DeclaringType).FindPrimaryKey()!.Properties));
}
///
@@ -2193,7 +2194,7 @@ public static void ForeignKeyChangeDetected(
{
definition.Log(
diagnostics,
- property.DeclaringEntityType.ShortName(),
+ property.DeclaringType.ShortName(),
property.Name);
}
@@ -2216,7 +2217,7 @@ private static string ForeignKeyChangeDetected(EventDefinitionBase definition, E
var d = (EventDefinition)definition;
var p = (PropertyChangedEventData)payload;
return d.GenerateMessage(
- p.Property.DeclaringEntityType.ShortName(),
+ p.Property.DeclaringType.ShortName(),
p.Property.Name);
}
@@ -2241,11 +2242,11 @@ public static void ForeignKeyChangeDetectedSensitive(
{
definition.Log(
diagnostics,
- property.DeclaringEntityType.ShortName(),
+ property.DeclaringType.ShortName(),
property.Name,
oldValue,
newValue,
- internalEntityEntry.BuildCurrentValuesString(property.DeclaringEntityType.FindPrimaryKey()!.Properties));
+ internalEntityEntry.BuildCurrentValuesString(((IEntityType)property.DeclaringType).FindPrimaryKey()!.Properties));
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -2267,11 +2268,12 @@ private static string ForeignKeyChangeDetectedSensitive(EventDefinitionBase defi
var d = (EventDefinition)definition;
var p = (PropertyChangedEventData)payload;
return d.GenerateMessage(
- p.Property.DeclaringEntityType.ShortName(),
+ p.Property.DeclaringType.ShortName(),
p.Property.Name,
p.OldValue,
p.NewValue,
- p.EntityEntry.GetInfrastructure().BuildCurrentValuesString(p.Property.DeclaringEntityType.FindPrimaryKey()!.Properties));
+ p.EntityEntry.GetInfrastructure().BuildCurrentValuesString(
+ ((IEntityType)p.Property.DeclaringType).FindPrimaryKey()!.Properties));
}
///
@@ -3073,6 +3075,37 @@ private static string MappedPropertyIgnoredWarning(EventDefinitionBase definitio
return d.GenerateMessage(p.Property.DeclaringType.ShortName(), p.Property.Name);
}
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The property.
+ public static void MappedComplexPropertyIgnoredWarning(
+ this IDiagnosticsLogger diagnostics,
+ IComplexProperty property)
+ {
+ var definition = CoreResources.LogMappedComplexPropertyIgnored(diagnostics);
+
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics, property.DeclaringType.ShortName(), property.Name);
+ }
+
+ if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = new ComplexPropertyEventData(definition, MappedComplexPropertyIgnoredWarning, property);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+ }
+ }
+
+ private static string MappedComplexPropertyIgnoredWarning(EventDefinitionBase definition, EventData payload)
+ {
+ var d = (EventDefinition)definition;
+ var p = (ComplexPropertyEventData)payload;
+ return d.GenerateMessage(p.Property.DeclaringType.ShortName(), p.Property.Name);
+ }
+
///
/// Logs for the event.
///
@@ -3378,7 +3411,7 @@ public static void ConflictingKeylessAndKeyAttributesWarning(
if (diagnostics.ShouldLog(definition))
{
- definition.Log(diagnostics, property.Name, property.DeclaringEntityType.DisplayName());
+ definition.Log(diagnostics, property.Name, property.DeclaringType.DisplayName());
}
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -3398,7 +3431,7 @@ private static string ConflictingKeylessAndKeyAttributesWarning(EventDefinitionB
var p = (PropertyEventData)payload;
return d.GenerateMessage(
p.Property.Name,
- p.Property.DeclaringEntityType.DisplayName());
+ p.Property.DeclaringType.DisplayName());
}
///
diff --git a/src/EFCore/Diagnostics/LoggingDefinitions.cs b/src/EFCore/Diagnostics/LoggingDefinitions.cs
index 389f76a963b..cbe8c5523ff 100644
--- a/src/EFCore/Diagnostics/LoggingDefinitions.cs
+++ b/src/EFCore/Diagnostics/LoggingDefinitions.cs
@@ -70,6 +70,15 @@ public abstract class LoggingDefinitions
[EntityFrameworkInternal]
public EventDefinitionBase? LogMappedPropertyIgnored;
+ ///
+ /// 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 EventDefinitionBase? LogMappedComplexPropertyIgnored;
+
///
/// 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/Infrastructure/AnnotatableBuilder.cs b/src/EFCore/Infrastructure/AnnotatableBuilder.cs
index 7bb11fdfdaf..b2e74bf565e 100644
--- a/src/EFCore/Infrastructure/AnnotatableBuilder.cs
+++ b/src/EFCore/Infrastructure/AnnotatableBuilder.cs
@@ -103,7 +103,7 @@ protected AnnotatableBuilder(TMetadata metadata, TModelBuilder modelBuilder)
object? value,
ConfigurationSource configurationSource)
=> value == null
- ? RemoveAnnotation(name, configurationSource)
+ ? HasNoAnnotation(name, configurationSource)
: HasAnnotation(name, value, configurationSource, canOverrideSameSource: true);
///
@@ -143,7 +143,7 @@ private static bool CanSetAnnotationValue(
/// The name of the annotation to remove.
/// The configuration source of the annotation to be set.
/// The same builder so that multiple calls can be chained.
- public virtual AnnotatableBuilder? RemoveAnnotation(
+ public virtual AnnotatableBuilder? HasNoAnnotation(
string name,
ConfigurationSource configurationSource)
{
@@ -238,7 +238,7 @@ bool IConventionAnnotatableBuilder.CanSetAnnotation(string name, object? value,
///
[DebuggerStepThrough]
IConventionAnnotatableBuilder? IConventionAnnotatableBuilder.HasNoAnnotation(string name, bool fromDataAnnotation)
- => RemoveAnnotation(name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ => HasNoAnnotation(name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
///
[DebuggerStepThrough]
diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs
index c014af1e32c..1598b9c3486 100644
--- a/src/EFCore/Infrastructure/ModelValidator.cs
+++ b/src/EFCore/Infrastructure/ModelValidator.cs
@@ -164,6 +164,7 @@ protected virtual void ValidatePropertyMapping(
clrProperties.ExceptWith(
((IEnumerable)entityType.GetProperties())
+ .Concat(entityType.GetComplexProperties())
.Concat(entityType.GetNavigations())
.Concat(entityType.GetSkipNavigations())
.Concat(entityType.GetServiceProperties()).Select(p => p.Name));
@@ -322,11 +323,11 @@ protected virtual void ValidateIgnoredMembers(
var property = entityType.FindProperty(ignoredMember);
if (property != null)
{
- if (property.DeclaringEntityType != entityType)
+ if (property.DeclaringType != entityType)
{
throw new InvalidOperationException(
CoreStrings.InheritedPropertyCannotBeIgnored(
- ignoredMember, entityType.DisplayName(), property.DeclaringEntityType.DisplayName()));
+ ignoredMember, entityType.DisplayName(), property.DeclaringType.DisplayName()));
}
Check.DebugFail("Should never get here...");
@@ -476,7 +477,7 @@ protected virtual void ValidateNoCycles(
}
///
- /// Validates the mapping/configuration of primary key nullability in the model.
+ /// Validates that all trackable entity types have a primary key.
///
/// The model to validate.
/// The logger to use.
@@ -898,7 +899,7 @@ protected virtual void ValidateTypeMappings(
throw new InvalidOperationException(
CoreStrings.ComparerPropertyMismatch(
providerComparer.Type.ShortDisplayName(),
- property.DeclaringEntityType.DisplayName(),
+ property.DeclaringType.DisplayName(),
property.Name,
actualProviderClrType.ShortDisplayName()));
}
diff --git a/src/EFCore/Internal/EntityFinder.cs b/src/EFCore/Internal/EntityFinder.cs
index 9eff76e904b..2e06583056c 100644
--- a/src/EFCore/Internal/EntityFinder.cs
+++ b/src/EFCore/Internal/EntityFinder.cs
@@ -94,7 +94,7 @@ public EntityFinder(
throw new ArgumentException(
CoreStrings.WrongGenericPropertyType(
_primaryKey.Properties[0].Name,
- _primaryKey.Properties[0].DeclaringEntityType.DisplayName(),
+ _primaryKey.Properties[0].DeclaringType.DisplayName(),
_primaryKeyType.ShortDisplayName(),
typeof(TKey).ShortDisplayName()));
}
diff --git a/src/EFCore/Metadata/Builders/CollectionNavigationBuilder.cs b/src/EFCore/Metadata/Builders/CollectionNavigationBuilder.cs
index 0f30401cf12..06067bc1fc7 100644
--- a/src/EFCore/Metadata/Builders/CollectionNavigationBuilder.cs
+++ b/src/EFCore/Metadata/Builders/CollectionNavigationBuilder.cs
@@ -268,7 +268,10 @@ protected virtual IMutableSkipNavigation WithLeftManyNavigation(string? inverseN
return ((EntityType)DeclaringEntityType).Builder.HasSkipNavigation(
navigationMember,
(EntityType)RelatedEntityType,
- ConfigurationSource.Explicit)!.Metadata;
+ foreignKey.PrincipalToDependent?.ClrType,
+ ConfigurationSource.Explicit,
+ collection: true,
+ onDependent: false)!.Metadata;
}
}
@@ -320,6 +323,7 @@ private IMutableSkipNavigation WithRightManyNavigation(MemberIdentity navigation
var skipNavigation = RelatedEntityType.FindSkipNavigation(navigationName);
if (skipNavigation != null)
{
+ ((SkipNavigation)skipNavigation).UpdateConfigurationSource(ConfigurationSource.Explicit);
return skipNavigation;
}
}
@@ -328,7 +332,9 @@ private IMutableSkipNavigation WithRightManyNavigation(MemberIdentity navigation
return ((EntityType)RelatedEntityType).Builder.HasSkipNavigation(
navigationMember,
(EntityType)DeclaringEntityType,
- ConfigurationSource.Explicit)!.Metadata;
+ ConfigurationSource.Explicit,
+ collection: true,
+ onDependent: false)!.Metadata;
}
}
diff --git a/src/EFCore/Metadata/Builders/ComplexPropertiesConfigurationBuilder.cs b/src/EFCore/Metadata/Builders/ComplexPropertiesConfigurationBuilder.cs
new file mode 100644
index 00000000000..5c14c9f6aa2
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexPropertiesConfigurationBuilder.cs
@@ -0,0 +1,74 @@
+// 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 Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+/// Provides a simple API surface for setting property defaults before conventions run.
+///
+///
+/// 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 entity types and relationships for more information and examples.
+///
+public class ComplexPropertiesConfigurationBuilder
+{
+ ///
+ /// 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 ComplexPropertiesConfigurationBuilder(ComplexPropertyConfiguration property)
+ {
+ Check.NotNull(property, nameof(property));
+
+ Configuration = property;
+ }
+
+ ///
+ /// 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]
+ protected virtual ComplexPropertyConfiguration Configuration { get; }
+
+ #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/ComplexPropertiesConfigurationBuilder`.cs b/src/EFCore/Metadata/Builders/ComplexPropertiesConfigurationBuilder`.cs
new file mode 100644
index 00000000000..21024c36705
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexPropertiesConfigurationBuilder`.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+/// Provides a simple API surface for setting property defaults before conventions run.
+///
+///
+/// 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 entity types and relationships for more information and examples.
+///
+public class ComplexPropertiesConfigurationBuilder : ComplexPropertiesConfigurationBuilder
+{
+ ///
+ /// 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 ComplexPropertiesConfigurationBuilder(ComplexPropertyConfiguration property)
+ : base(property)
+ {
+ }
+}
diff --git a/src/EFCore/Metadata/Builders/ComplexPropertyBuilder.cs b/src/EFCore/Metadata/Builders/ComplexPropertyBuilder.cs
new file mode 100644
index 00000000000..9fbaf953795
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexPropertyBuilder.cs
@@ -0,0 +1,486 @@
+// 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 an .
+///
+///
+/// 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 entity types and relationships for more information and examples.
+///
+public class ComplexPropertyBuilder :
+ IInfrastructure, 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 ComplexPropertyBuilder(IMutableComplexProperty complexProperty)
+ {
+ PropertyBuilder = ((ComplexProperty)complexProperty).Builder;
+ TypeBuilder = ((ComplexProperty)complexProperty).ComplexType.Builder;
+ }
+
+ ///
+ /// 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]
+ protected virtual InternalComplexPropertyBuilder PropertyBuilder { [DebuggerStepThrough] get; }
+
+ ///
+ /// 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]
+ protected virtual InternalComplexTypeBuilder TypeBuilder { [DebuggerStepThrough] get; }
+
+ ///
+ /// Gets the internal builder being used to configure the complex property.
+ ///
+ IConventionComplexPropertyBuilder IInfrastructure.Instance
+ => PropertyBuilder;
+
+ ///
+ /// Gets the internal builder being used to configure the complex type.
+ ///
+ IConventionComplexTypeBuilder IInfrastructure.Instance
+ => TypeBuilder;
+
+ ///
+ /// The complex property being configured.
+ ///
+ public virtual IMutableComplexProperty Metadata
+ => PropertyBuilder.Metadata;
+
+ ///
+ /// Adds or updates an annotation on the complex 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 ComplexPropertyBuilder HasPropertyAnnotation(string annotation, object? value)
+ {
+ Check.NotEmpty(annotation, nameof(annotation));
+
+ PropertyBuilder.HasAnnotation(annotation, value, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Adds or updates an annotation on the complex type. 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 ComplexPropertyBuilder HasTypeAnnotation(string annotation, object? value)
+ {
+ Check.NotEmpty(annotation, nameof(annotation));
+
+ TypeBuilder.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 ComplexPropertyBuilder IsRequired(bool required = true)
+ {
+ PropertyBuilder.IsRequired(required, 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 ComplexPropertyBuilder HasField(string fieldName)
+ {
+ Check.NotEmpty(fieldName, nameof(fieldName));
+
+ PropertyBuilder.HasField(fieldName, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Returns an object that can be used to configure a property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property with this overload the property name must match the
+ /// name of a CLR property or field on the complex type. This overload cannot be used to
+ /// add a new shadow state property.
+ ///
+ /// The name of the property to be configured.
+ /// An object that can be used to configure the property.
+ public virtual ComplexTypePropertyBuilder Property(string propertyName)
+ => new(
+ TypeBuilder.Property(
+ Check.NotEmpty(propertyName, nameof(propertyName)),
+ ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Returns an object that can be used to configure a property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the entity class
+ /// then it will be added to the model. If no property exists in the entity class, then
+ /// a new shadow state property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the entity class. The current value for the property is stored in
+ /// the rather than being stored in instances of the entity class.
+ ///
+ /// 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)
+ => new(
+ TypeBuilder.Property(
+ typeof(TProperty),
+ Check.NotEmpty(propertyName, nameof(propertyName)), ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Returns an object that can be used to configure a property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the entity class
+ /// then it will be added to the model. If no property exists in the entity class, then
+ /// a new shadow state property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the entity class. The current value for the property is stored in
+ /// the rather than being stored in instances of the entity class.
+ ///
+ /// 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)
+ => new(
+ TypeBuilder.Property(
+ Check.NotNull(propertyType, nameof(propertyType)),
+ Check.NotEmpty(propertyName, nameof(propertyName)), ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Returns an object that can be used to configure a property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// Indexer properties are stored in the entity using
+ /// an indexer
+ /// supplying the provided property name.
+ ///
+ /// 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
+ <[DynamicallyAccessedMembers(IProperty.DynamicallyAccessedMemberTypes)] TProperty>(string propertyName)
+ => new(
+ TypeBuilder.IndexerProperty(
+ typeof(TProperty),
+ Check.NotEmpty(propertyName, nameof(propertyName)), ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Returns an object that can be used to configure a property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// Indexer properties are stored in the entity using
+ /// an indexer
+ /// supplying the provided property name.
+ ///
+ /// 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(
+ [DynamicallyAccessedMembers(IProperty.DynamicallyAccessedMemberTypes)] Type propertyType,
+ string propertyName)
+ {
+ Check.NotNull(propertyType, nameof(propertyType));
+
+ return new(
+ TypeBuilder.IndexerProperty(
+ propertyType,
+ Check.NotEmpty(propertyName, nameof(propertyName)), ConfigurationSource.Explicit)!.Metadata);
+ }
+
+ ///
+ /// Returns an object that can be used to configure a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property with this overload the property name must match the
+ /// name of a CLR property or field on the complex type. This overload cannot be used to
+ /// add a new shadow state complex property.
+ ///
+ /// The name of the property to be configured.
+ /// An object that can be used to configure the property.
+ public virtual ComplexPropertyBuilder ComplexProperty(string propertyName)
+ => new(
+ TypeBuilder.ComplexProperty(
+ propertyType: null,
+ Check.NotEmpty(propertyName, nameof(propertyName)),
+ collection: false,
+ ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Returns an object that can be used to configure a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// 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 ComplexPropertyBuilder ComplexProperty(string propertyName)
+ => new(
+ TypeBuilder.ComplexProperty(
+ typeof(TProperty),
+ Check.NotEmpty(propertyName, nameof(propertyName)),
+ collection: false,
+ ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Returns an object that can be used to configure a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new complex property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// 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 ComplexPropertyBuilder ComplexProperty(Type propertyType, string propertyName)
+ => new(
+ TypeBuilder.ComplexProperty(
+ Check.NotNull(propertyType, nameof(propertyType)),
+ Check.NotEmpty(propertyName, nameof(propertyName)),
+ collection: false,
+ ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Configures a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property with this overload the property name must match the
+ /// name of a CLR property or field on the complex type. This overload cannot be used to
+ /// add a new shadow state complex property.
+ ///
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexPropertyBuilder ComplexProperty(string propertyName, Action buildAction)
+ {
+ Check.NotNull(buildAction, nameof(buildAction));
+
+ buildAction(ComplexProperty(propertyName));
+
+ return this;
+ }
+
+ ///
+ /// Configures a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexPropertyBuilder ComplexProperty(
+ string propertyName, Action> buildAction)
+ {
+ Check.NotNull(buildAction, nameof(buildAction));
+
+ buildAction(ComplexProperty(propertyName));
+
+ return this;
+ }
+
+ ///
+ /// Configures a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new complex property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexPropertyBuilder ComplexProperty(Type propertyType, string propertyName, Action buildAction)
+ {
+ Check.NotNull(buildAction, nameof(buildAction));
+
+ buildAction(ComplexProperty(propertyType, propertyName));
+
+ return this;
+ }
+
+ ///
+ /// Excludes the given property from the complex type. This method is typically used to remove properties
+ /// and navigations from the complex type that were added by convention.
+ ///
+ /// The name of the property to be removed from the complex type.
+ public virtual ComplexPropertyBuilder Ignore(string propertyName)
+ {
+ Check.NotEmpty(propertyName, nameof(propertyName));
+
+ TypeBuilder.Ignore(propertyName, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the to be used for this entity type.
+ /// This strategy indicates how the context detects changes to properties for an instance of the entity type.
+ ///
+ /// The change tracking strategy to be used.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexPropertyBuilder HasChangeTrackingStrategy(ChangeTrackingStrategy changeTrackingStrategy)
+ {
+ TypeBuilder.HasChangeTrackingStrategy(changeTrackingStrategy, 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
+ /// entity type or model.
+ ///
+ ///
+ /// The to use for this property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexPropertyBuilder UsePropertyAccessMode(PropertyAccessMode propertyAccessMode)
+ {
+ PropertyBuilder.UsePropertyAccessMode(propertyAccessMode, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Sets the to use for all properties of this complex type.
+ ///
+ ///
+ ///
+ /// 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 all properties of this complex type as described in the enum.
+ ///
+ ///
+ /// Calling this method overrides for all properties of this complex type any access mode that was
+ /// set on the model.
+ ///
+ ///
+ /// The to use for properties of this complex type.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexPropertyBuilder UseDefaultPropertyAccessMode(PropertyAccessMode propertyAccessMode)
+ {
+ TypeBuilder.UsePropertyAccessMode(propertyAccessMode, 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/ComplexPropertyBuilder`.cs b/src/EFCore/Metadata/Builders/ComplexPropertyBuilder`.cs
new file mode 100644
index 00000000000..868b5645c87
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexPropertyBuilder`.cs
@@ -0,0 +1,275 @@
+// 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;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+/// Provides a simple API for configuring an .
+///
+///
+/// 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 entity types and relationships for more information and examples.
+///
+/// The complex type being configured.
+public class ComplexPropertyBuilder<[DynamicallyAccessedMembers(IEntityType.DynamicallyAccessedMemberTypes)] TComplex>
+ : ComplexPropertyBuilder
+{
+ ///
+ /// 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 ComplexPropertyBuilder(IMutableComplexProperty complexProperty)
+ : base(complexProperty)
+ {
+ }
+
+ ///
+ /// Adds or updates an annotation on the entity type. 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 typeBuilder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexPropertyBuilder HasPropertyAnnotation(string annotation, object? value)
+ => (ComplexPropertyBuilder)base.HasPropertyAnnotation(annotation, value);
+
+ ///
+ /// Adds or updates an annotation on the entity type. 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 typeBuilder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexPropertyBuilder HasTypeAnnotation(string annotation, object? value)
+ => (ComplexPropertyBuilder)base.HasTypeAnnotation(annotation, value);
+
+ ///
+ /// 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 new virtual ComplexPropertyBuilder IsRequired(bool required = true)
+ => (ComplexPropertyBuilder)base.IsRequired(required);
+
+ ///
+ /// 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 ComplexPropertyBuilder HasField(string fieldName)
+ => (ComplexPropertyBuilder)base.HasField(fieldName);
+
+ ///
+ /// Returns an object that can be used to configure a property of the entity type.
+ /// If the specified property is not already part of the model, it will be added.
+ ///
+ ///
+ /// A lambda expression representing the property to be configured (
+ /// blog => blog.Url).
+ ///
+ /// An object that can be used to configure the property.
+ public virtual ComplexTypePropertyBuilder Property(Expression> propertyExpression)
+ => new(TypeBuilder.Property(
+ Check.NotNull(propertyExpression, nameof(propertyExpression)).GetMemberAccess(), ConfigurationSource.Explicit)!
+ .Metadata);
+
+ ///
+ /// Configures a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property with this overload the property name must match the
+ /// name of a CLR property or field on the complex type. This overload cannot be used to
+ /// add a new shadow state complex property.
+ ///
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexPropertyBuilder ComplexProperty(string propertyName, Action buildAction)
+ => (ComplexPropertyBuilder)base.ComplexProperty(propertyName, buildAction);
+
+ ///
+ /// Configures a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexPropertyBuilder ComplexProperty(
+ string propertyName, Action> buildAction)
+ => (ComplexPropertyBuilder)base.ComplexProperty(propertyName, buildAction);
+
+ ///
+ /// Configures a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new complex property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexPropertyBuilder ComplexProperty(
+ Type propertyType, string propertyName, Action buildAction)
+ => (ComplexPropertyBuilder)base.ComplexProperty(propertyType, propertyName, buildAction);
+
+ ///
+ /// Returns an object that can be used to configure a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ ///
+ /// A lambda expression representing the property to be configured (
+ /// blog => blog.Url).
+ ///
+ /// An object that can be used to configure the property.
+ public virtual ComplexPropertyBuilder ComplexProperty(Expression> propertyExpression)
+ => new(TypeBuilder.ComplexProperty(
+ Check.NotNull(propertyExpression, nameof(propertyExpression)).GetMemberAccess(),
+ collection: false,
+ ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Configures a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ ///
+ /// A lambda expression representing the property to be configured (
+ /// blog => blog.Url).
+ ///
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexPropertyBuilder ComplexProperty(
+ Expression> propertyExpression, Action> buildAction)
+ {
+ Check.NotNull(buildAction, nameof(buildAction));
+
+ buildAction(ComplexProperty(propertyExpression));
+
+ return this;
+ }
+
+ ///
+ /// Excludes the given property from the entity type. This method is typically used to remove properties
+ /// or navigations from the entity type that were added by convention.
+ ///
+ ///
+ /// A lambda expression representing the property to be ignored
+ /// (blog => blog.Url).
+ ///
+ public virtual ComplexPropertyBuilder Ignore(Expression> propertyExpression)
+ => (ComplexPropertyBuilder)base.Ignore(
+ Check.NotNull(propertyExpression, nameof(propertyExpression)).GetMemberAccess().GetSimpleMemberName());
+
+ ///
+ /// Excludes the given property from the entity type. This method is typically used to remove properties
+ /// or navigations from the entity type that were added by convention.
+ ///
+ /// The name of the property to be removed from the entity type.
+ public new virtual ComplexPropertyBuilder Ignore(string propertyName)
+ => (ComplexPropertyBuilder)base.Ignore(propertyName);
+
+ ///
+ /// Configures the to be used for this entity type.
+ /// This strategy indicates how the context detects changes to properties for an instance of the entity type.
+ ///
+ /// The change tracking strategy to be used.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexPropertyBuilder HasChangeTrackingStrategy(ChangeTrackingStrategy changeTrackingStrategy)
+ => (ComplexPropertyBuilder)base.HasChangeTrackingStrategy(changeTrackingStrategy);
+
+ ///
+ /// 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
+ /// entity type or model.
+ ///
+ ///
+ /// The to use for this property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexPropertyBuilder UsePropertyAccessMode(PropertyAccessMode propertyAccessMode)
+ => (ComplexPropertyBuilder)base.UsePropertyAccessMode(propertyAccessMode);
+
+ ///
+ /// Sets the to use for all properties of this entity type.
+ ///
+ ///
+ ///
+ /// 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 all properties of this entity type as described in the enum.
+ ///
+ ///
+ /// Calling this method overrides for all properties of this entity type any access mode that was
+ /// set on the model.
+ ///
+ ///
+ /// The to use for properties of this entity type.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexPropertyBuilder UseDefaultPropertyAccessMode(PropertyAccessMode propertyAccessMode)
+ => (ComplexPropertyBuilder)base.UseDefaultPropertyAccessMode(propertyAccessMode);
+}
diff --git a/src/EFCore/Metadata/Builders/ComplexTypePropertyBuilder.cs b/src/EFCore/Metadata/Builders/ComplexTypePropertyBuilder.cs
new file mode 100644
index 00000000000..b6c57e0effb
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexTypePropertyBuilder.cs
@@ -0,0 +1,695 @@
+// 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 .
+///
+///
+///
+/// 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 ComplexTypePropertyBuilder : 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 ComplexTypePropertyBuilder(IMutableProperty property)
+ {
+ Check.NotNull(property, nameof(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 ComplexTypePropertyBuilder HasAnnotation(string annotation, object? value)
+ {
+ Check.NotEmpty(annotation, nameof(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 ComplexTypePropertyBuilder IsRequired(bool required = true)
+ {
+ Builder.IsRequired(required, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the maximum length of data that can be stored in this property.
+ /// Maximum length can only be set on array properties (including properties).
+ ///
+ ///
+ /// The maximum length of data allowed in the property. A value of -1 indicates that the property has no maximum length.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder HasMaxLength(int maxLength)
+ {
+ Builder.HasMaxLength(maxLength, 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 ComplexTypePropertyBuilder HasSentinel(object? sentinel)
+ {
+ Builder.HasSentinel(sentinel, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the precision and scale of the property.
+ ///
+ /// The precision of the property.
+ /// The scale of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder HasPrecision(int precision, int scale)
+ {
+ Builder.HasPrecision(precision, ConfigurationSource.Explicit);
+ Builder.HasScale(scale, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the precision of the property.
+ ///
+ /// The precision of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder HasPrecision(int precision)
+ {
+ Builder.HasPrecision(precision, 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 ComplexTypePropertyBuilder IsUnicode(bool unicode = true)
+ {
+ Builder.IsUnicode(unicode, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures the property as and
+ /// .
+ ///
+ ///
+ /// Database providers can choose to interpret this in different way, but it is commonly used
+ /// to indicate some form of automatic row-versioning as used for optimistic concurrency detection.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder IsRowVersion()
+ {
+ Builder.ValueGenerated(ValueGenerated.OnAddOrUpdate, ConfigurationSource.Explicit);
+ Builder.IsConcurrencyToken(true, 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder HasValueGenerator(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? valueGeneratorType)
+ {
+ Builder.HasValueGenerator(valueGeneratorType, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures a factory 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.).
+ ///
+ ///
+ /// This factory will be invoked once to create a single instance of the value generator, and
+ /// this will be used to generate values for this property in all instances of the complex type.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// A delegate that will be used to create value generator instances.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder HasValueGenerator(Func factory)
+ {
+ Check.NotNull(factory, nameof(factory));
+
+ Builder.HasValueGenerator(factory, 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder HasValueGeneratorFactory(
+ [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] Type? valueGeneratorFactoryType)
+ {
+ Builder.HasValueGeneratorFactory(valueGeneratorFactoryType, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures whether this property should be used as a concurrency token. When a property is configured
+ /// as a concurrency token the value in the database will be checked when an instance of this complex type
+ /// is updated or deleted during to ensure it has not changed since
+ /// the instance was retrieved from the database. If it has changed, an exception will be thrown and the
+ /// changes will not be applied to the database.
+ ///
+ /// A value indicating whether this property is a concurrency token.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder IsConcurrencyToken(bool concurrencyToken = true)
+ {
+ Builder.IsConcurrencyToken(concurrencyToken, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures a property to never have a value generated by the database when an instance of this
+ /// complex type is saved.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ ///
+ /// Note that values may still be generated by a client-side value generator, if one is set explicitly or by a convention.
+ ///
+ public virtual ComplexTypePropertyBuilder ValueGeneratedNever()
+ {
+ Builder.ValueGenerated(ValueGenerated.Never, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures a property to have a value generated only when saving a new entity, unless a non-null,
+ /// non-temporary value has been set, in which case the set value will be saved instead. The value
+ /// may be generated by a client-side value generator or may be generated by the database as part
+ /// of saving the entity.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder ValueGeneratedOnAdd()
+ {
+ Builder.ValueGenerated(ValueGenerated.OnAdd, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures a property to have a value generated when saving a new or existing entity.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder ValueGeneratedOnAddOrUpdate()
+ {
+ Builder.ValueGenerated(ValueGenerated.OnAddOrUpdate, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures a property to have a value generated when saving an existing entity.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder ValueGeneratedOnUpdate()
+ {
+ Builder.ValueGenerated(ValueGenerated.OnUpdate, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ /// Configures a property to have a value generated under certain conditions when saving an existing entity.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexTypePropertyBuilder ValueGeneratedOnUpdateSometimes()
+ {
+ Builder.ValueGenerated(ValueGenerated.OnUpdateSometimes, 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 ComplexTypePropertyBuilder HasField(string fieldName)
+ {
+ Check.NotEmpty(fieldName, nameof(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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type conversionType,
+ ValueComparer? valueComparer)
+ => HasConversion(conversionType, valueComparer, null);
+
+ // DynamicallyAccessedMemberTypes.PublicParameterlessConstructor
+ ///
+ /// 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 ComplexTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type conversionType,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ {
+ Check.NotNull(conversionType, nameof(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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type conversionType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? providerComparerType)
+ {
+ Check.NotNull(conversionType, nameof(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/ComplexTypePropertyBuilder`.cs b/src/EFCore/Metadata/Builders/ComplexTypePropertyBuilder`.cs
new file mode 100644
index 00000000000..7f0e24503b5
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexTypePropertyBuilder`.cs
@@ -0,0 +1,610 @@
+// 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 .
+///
+///
+///
+/// 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 ComplexTypePropertyBuilder : ComplexTypePropertyBuilder
+{
+ ///
+ /// 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 ComplexTypePropertyBuilder(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 ComplexTypePropertyBuilder HasAnnotation(string annotation, object? value)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder IsRequired(bool required = true)
+ => (ComplexTypePropertyBuilder)base.IsRequired(required);
+
+ ///
+ /// Configures the maximum length of data that can be stored in this property.
+ /// Maximum length can only be set on array properties (including properties).
+ ///
+ ///
+ /// The maximum length of data allowed in the property. A value of -1 indicates that the property has no maximum length.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder HasMaxLength(int maxLength)
+ => (ComplexTypePropertyBuilder)base.HasMaxLength(maxLength);
+
+ ///
+ /// 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 ComplexTypePropertyBuilder HasSentinel(object? sentinel)
+ => (ComplexTypePropertyBuilder)base.HasSentinel(sentinel);
+
+ ///
+ /// Configures the precision and scale of the property.
+ ///
+ /// The precision of the property.
+ /// The scale of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder HasPrecision(int precision, int scale)
+ => (ComplexTypePropertyBuilder)base.HasPrecision(precision, scale);
+
+ ///
+ /// Configures the precision of the property.
+ ///
+ /// The precision of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder HasPrecision(int precision)
+ => (ComplexTypePropertyBuilder)base.HasPrecision(precision);
+
+ ///
+ /// 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 ComplexTypePropertyBuilder IsUnicode(bool unicode = true)
+ => (ComplexTypePropertyBuilder)base.IsUnicode(unicode);
+
+ ///
+ /// Configures the property as and
+ /// .
+ ///
+ ///
+ /// Database providers can choose to interpret this in different way, but it is commonly used
+ /// to indicate some form of automatic row-versioning as used for optimistic concurrency detection.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder IsRowVersion()
+ => (ComplexTypePropertyBuilder)base.IsRowVersion();
+
+ ///
+ /// 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 ComplexTypePropertyBuilder HasValueGenerator
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TGenerator>()
+ where TGenerator : ValueGenerator
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasValueGenerator(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? valueGeneratorType)
+ => (ComplexTypePropertyBuilder)base.HasValueGenerator(valueGeneratorType);
+
+ ///
+ /// Configures a factory 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.).
+ ///
+ ///
+ /// This factory will be invoked once to create a single instance of the value generator, and
+ /// this will be used to generate values for this property in all instances of the complex type.
+ ///
+ ///
+ /// This method is intended for use with custom value generation. Value generation for common cases is
+ /// usually handled automatically by the database provider.
+ ///
+ ///
+ /// A delegate that will be used to create value generator instances.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder HasValueGenerator(Func factory)
+ => (ComplexTypePropertyBuilder)base.HasValueGenerator(factory);
+
+ ///
+ /// 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 ComplexTypePropertyBuilder HasValueGeneratorFactory
+ <[DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] TFactory>()
+ where TFactory : ValueGeneratorFactory
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasValueGeneratorFactory(
+ [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] Type? valueGeneratorFactoryType)
+ => (ComplexTypePropertyBuilder)base.HasValueGeneratorFactory(valueGeneratorFactoryType);
+
+ ///
+ /// Configures whether this property should be used as a concurrency token. When a property is configured
+ /// as a concurrency token the value in the database will be checked when an instance of this complex type
+ /// is updated or deleted during to ensure it has not changed since
+ /// the instance was retrieved from the database. If it has changed, an exception will be thrown and the
+ /// changes will not be applied to the database.
+ ///
+ /// A value indicating whether this property is a concurrency token.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder IsConcurrencyToken(bool concurrencyToken = true)
+ => (ComplexTypePropertyBuilder)base.IsConcurrencyToken(concurrencyToken);
+
+ ///
+ /// Configures a property to never have a value generated when an instance of this
+ /// complex type is saved.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ ///
+ /// Note that temporary values may still be generated for use internally before a
+ /// new entity is saved.
+ ///
+ public new virtual ComplexTypePropertyBuilder ValueGeneratedNever()
+ => (ComplexTypePropertyBuilder)base.ValueGeneratedNever();
+
+ ///
+ /// Configures a property to have a value generated only when saving a new entity, unless a non-null,
+ /// non-temporary value has been set, in which case the set value will be saved instead. The value
+ /// may be generated by a client-side value generator or may be generated by the database as part
+ /// of saving the entity.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder ValueGeneratedOnAdd()
+ => (ComplexTypePropertyBuilder)base.ValueGeneratedOnAdd();
+
+ ///
+ /// Configures a property to have a value generated when saving a new or existing entity.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder ValueGeneratedOnAddOrUpdate()
+ => (ComplexTypePropertyBuilder)base.ValueGeneratedOnAddOrUpdate();
+
+ ///
+ /// Configures a property to have a value generated when saving an existing entity.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder ValueGeneratedOnUpdate()
+ => (ComplexTypePropertyBuilder)base.ValueGeneratedOnUpdate();
+
+ ///
+ /// Configures a property to have a value generated under certain conditions when saving an existing entity.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexTypePropertyBuilder ValueGeneratedOnUpdateSometimes()
+ => (ComplexTypePropertyBuilder)base.ValueGeneratedOnUpdateSometimes();
+
+ ///
+ /// 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 ComplexTypePropertyBuilder HasField(string fieldName)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder UsePropertyAccessMode(PropertyAccessMode propertyAccessMode)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TConversion>()
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? providerClrType)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion(
+ Expression> convertToProviderExpression,
+ Expression> convertFromProviderExpression)
+ => HasConversion(
+ new ValueConverter(
+ Check.NotNull(convertToProviderExpression, nameof(convertToProviderExpression)),
+ Check.NotNull(convertFromProviderExpression, nameof(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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder HasConversion(ValueConverter? converter)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TConversion>(
+ ValueComparer? valueComparer)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TConversion>(
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type conversionType,
+ ValueComparer? valueComparer)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type conversionType,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion(
+ Expression> convertToProviderExpression,
+ Expression> convertFromProviderExpression,
+ ValueComparer? valueComparer)
+ => HasConversion(
+ new ValueConverter(
+ Check.NotNull(convertToProviderExpression, nameof(convertToProviderExpression)),
+ Check.NotNull(convertFromProviderExpression, nameof(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 ComplexTypePropertyBuilder HasConversion(
+ Expression> convertToProviderExpression,
+ Expression> convertFromProviderExpression,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => HasConversion(
+ new ValueConverter(
+ Check.NotNull(convertToProviderExpression, nameof(convertToProviderExpression)),
+ Check.NotNull(convertFromProviderExpression, nameof(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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder 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 ComplexTypePropertyBuilder HasConversion(
+ ValueConverter? converter,
+ ValueComparer? valueComparer)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion(
+ ValueConverter? converter,
+ ValueComparer? valueComparer,
+ ValueComparer? providerComparer)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TConversion,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TComparer>()
+ where TComparer : ValueComparer
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TConversion,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TComparer,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TProviderComparer>()
+ where TComparer : ValueComparer
+ where TProviderComparer : ValueComparer
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type conversionType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType)
+ => (ComplexTypePropertyBuilder)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 ComplexTypePropertyBuilder HasConversion(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type conversionType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? providerComparerType)
+ => (ComplexTypePropertyBuilder)base.HasConversion(conversionType, comparerType, providerComparerType);
+}
diff --git a/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs b/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs
index 4937b1b67ee..65299bcc607 100644
--- a/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs
+++ b/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs
@@ -31,7 +31,14 @@ public EntityTypeBuilder(IMutableEntityType entityType)
Builder = ((EntityType)entityType).Builder;
}
- private InternalEntityTypeBuilder Builder { [DebuggerStepThrough] get; }
+ ///
+ /// 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]
+ protected virtual InternalEntityTypeBuilder Builder { [DebuggerStepThrough] get; }
///
/// Gets the internal builder being used to configure the entity type.
@@ -205,6 +212,139 @@ public virtual PropertyBuilder IndexerProperty(
Check.NotEmpty(propertyName, nameof(propertyName)), ConfigurationSource.Explicit)!.Metadata);
}
+ ///
+ /// Returns an object that can be used to configure a complex property of the entity type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property with this overload the property name must match the
+ /// name of a CLR property or field on the entity type. This overload cannot be used to
+ /// add a new shadow state complex property.
+ ///
+ /// The name of the property to be configured.
+ /// An object that can be used to configure the property.
+ public virtual ComplexPropertyBuilder ComplexProperty(string propertyName)
+ => new(
+ Builder.ComplexProperty(
+ propertyType: null,
+ Check.NotEmpty(propertyName, nameof(propertyName)),
+ collection: false,
+ ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Returns an object that can be used to configure a complex property of the entity type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the entity class
+ /// then it will be added to the model. If no property exists in the entity class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the entity class. The current value for the property is stored in
+ /// the rather than being stored in instances of the entity class.
+ ///
+ /// 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 ComplexPropertyBuilder ComplexProperty(string propertyName)
+ => new(
+ Builder.ComplexProperty(
+ typeof(TProperty),
+ Check.NotEmpty(propertyName, nameof(propertyName)),
+ collection: false,
+ ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Configures a complex property of the entity type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new complex property, if a property with the same name exists in the entity class
+ /// then it will be added to the model. If no property exists in the entity class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the entity class. The current value for the property is stored in
+ /// the rather than being stored in instances of the entity class.
+ ///
+ /// 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 ComplexPropertyBuilder ComplexProperty(Type propertyType, string propertyName)
+ => new(
+ Builder.ComplexProperty(
+ Check.NotNull(propertyType, nameof(propertyType)),
+ Check.NotEmpty(propertyName, nameof(propertyName)),
+ collection: false,
+ ConfigurationSource.Explicit)!.Metadata);
+
+ ///
+ /// Configures a complex property of the entity type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property with this overload the property name must match the
+ /// name of a CLR property or field on the complex type. This overload cannot be used to
+ /// add a new shadow state complex property.
+ ///
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual EntityTypeBuilder ComplexProperty(string propertyName, Action buildAction)
+ {
+ Check.NotNull(buildAction, nameof(buildAction));
+
+ buildAction(ComplexProperty(propertyName));
+
+ return this;
+ }
+
+ ///
+ /// Configures a complex property of the entity type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual EntityTypeBuilder ComplexProperty(
+ string propertyName, Action> buildAction)
+ {
+ Check.NotNull(buildAction, nameof(buildAction));
+
+ buildAction(ComplexProperty(propertyName));
+
+ return this;
+ }
+
+ ///
+ /// Returns an object that can be used to configure a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new complex property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual EntityTypeBuilder ComplexProperty(Type propertyType, string propertyName, Action buildAction)
+ {
+ Check.NotNull(buildAction, nameof(buildAction));
+
+ buildAction(ComplexProperty(propertyType, propertyName));
+
+ return this;
+ }
+
///
/// Returns an object that can be used to configure an existing navigation property of the entity type.
/// It is an error for the navigation property not to exist.
diff --git a/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs b/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs
index 1f2c5ae82d2..5e4061ab41a 100644
--- a/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs
+++ b/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs
@@ -152,6 +152,97 @@ public virtual PropertyBuilder Property(Expression
+ /// Configures a complex property of the entity type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property with this overload the property name must match the
+ /// name of a CLR property or field on the complex type. This overload cannot be used to
+ /// add a new shadow state complex property.
+ ///
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual EntityTypeBuilder ComplexProperty(string propertyName, Action buildAction)
+ => (EntityTypeBuilder)base.ComplexProperty(propertyName, buildAction);
+
+ ///
+ /// Configures a complex property of the entity type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual EntityTypeBuilder ComplexProperty(
+ string propertyName, Action> buildAction)
+ => (EntityTypeBuilder)base.ComplexProperty(propertyName, buildAction);
+
+ ///
+ /// Returns an object that can be used to configure a complex property of the complex type.
+ /// If no property with the given name exists, then a new property will be added.
+ ///
+ ///
+ /// When adding a new complex property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
+ /// a new shadow state complex property will be added. A shadow state property is one that does not have a
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
+ ///
+ /// The type of the property to be configured.
+ /// The name of the property to be configured.
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual EntityTypeBuilder ComplexProperty(
+ Type propertyType, string propertyName, Action buildAction)
+ => (EntityTypeBuilder)base.ComplexProperty(propertyType, propertyName, buildAction);
+
+ ///
+ /// Returns an object that can be used to configure a complex property of the entity type.
+ /// If the specified property is not already part of the model, it will be added.
+ ///
+ ///
+ /// A lambda expression representing the property to be configured (
+ /// blog => blog.Url).
+ ///
+ /// An object that can be used to configure the complex property.
+ public virtual ComplexPropertyBuilder ComplexProperty(
+ Expression> propertyExpression)
+ => new(
+ Builder.ComplexProperty(
+ Check.NotNull(propertyExpression, nameof(propertyExpression)).GetMemberAccess(),
+ collection: false,
+ ConfigurationSource.Explicit)!
+ .Metadata);
+
+ ///
+ /// Configures a complex property of the entity type.
+ /// If the specified property is not already part of the model, it will be added.
+ ///
+ ///
+ /// A lambda expression representing the property to be configured (
+ /// blog => blog.Url).
+ ///
+ /// An action that performs configuration of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual EntityTypeBuilder ComplexProperty(
+ Expression> propertyExpression, Action> buildAction)
+ {
+ Check.NotNull(buildAction, nameof(buildAction));
+
+ buildAction(ComplexProperty(propertyExpression));
+
+ return this;
+ }
+
///
/// Returns an object that can be used to configure an existing navigation property of the entity type.
/// It is an error for the navigation property not to exist.
@@ -1397,7 +1488,4 @@ public virtual DiscriminatorBuilder HasDiscriminatorThe same builder instance so that multiple configuration calls can be chained.
public new virtual EntityTypeBuilder HasNoDiscriminator()
=> (EntityTypeBuilder)base.HasNoDiscriminator();
-
- private InternalEntityTypeBuilder Builder
- => (InternalEntityTypeBuilder)this.GetInfrastructure();
}
diff --git a/src/EFCore/Metadata/Builders/IConventionComplexPropertyBuilder.cs b/src/EFCore/Metadata/Builders/IConventionComplexPropertyBuilder.cs
new file mode 100644
index 00000000000..f389efee52c
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/IConventionComplexPropertyBuilder.cs
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+///
+/// Provides a simple API surface for configuring an from conventions.
+///
+///
+/// This interface is typically used by database providers (and other extensions). It is generally
+/// not used in application code.
+///
+///
+///
+/// See Model building conventions for more information and examples.
+///
+public interface IConventionComplexPropertyBuilder : IConventionPropertyBaseBuilder
+{
+ ///
+ /// Gets the property being configured.
+ ///
+ new IConventionComplexProperty Metadata { get; }
+
+ ///
+ /// 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.
+ /// to reset to default.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the requiredness was configured,
+ /// otherwise.
+ ///
+ IConventionComplexPropertyBuilder? IsRequired(bool? required, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether this property requiredness can be configured
+ /// from the current configuration source.
+ ///
+ ///
+ /// A value indicating whether the property is required.
+ /// to reset to default.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the property requiredness can be configured.
+ bool CanSetIsRequired(bool? required, bool fromDataAnnotation = false);
+}
diff --git a/src/EFCore/Metadata/Builders/IConventionComplexTypeBuilder.cs b/src/EFCore/Metadata/Builders/IConventionComplexTypeBuilder.cs
new file mode 100644
index 00000000000..f2558141ad4
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/IConventionComplexTypeBuilder.cs
@@ -0,0 +1,137 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+///
+/// Provides a simple API surface for configuring an from conventions.
+///
+///
+/// This interface is typically used by database providers (and other extensions). It is generally
+/// not used in application code.
+///
+///
+///
+/// See Model building conventions for more information and examples.
+///
+public interface IConventionComplexTypeBuilder : IConventionTypeBaseBuilder
+{
+ ///
+ /// Gets the property being configured.
+ ///
+ new IConventionComplexType Metadata { get; }
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionComplexTypeBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionComplexTypeBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionComplexTypeBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
+ ///
+ /// Removes properties in the given list if they are not part of any metadata object.
+ ///
+ /// The properties to remove.
+ new IConventionComplexTypeBuilder RemoveUnusedImplicitProperties(IReadOnlyList properties);
+
+ ///
+ /// Removes a property from this complex type.
+ ///
+ /// The property to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the property was removed,
+ /// otherwise.
+ ///
+ new IConventionComplexTypeBuilder? HasNoProperty(IConventionProperty property, bool fromDataAnnotation = false);
+
+ ///
+ /// Removes a complex property from this complex type.
+ ///
+ /// The complex property to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the complex property was removed,
+ /// otherwise.
+ ///
+ new IConventionComplexTypeBuilder? HasNoComplexProperty(IConventionComplexProperty complexProperty, bool fromDataAnnotation = false);
+
+ ///
+ /// Excludes the given property from the complex type and prevents conventions from adding a matching property
+ /// or navigation to the type.
+ ///
+ /// The name of the member to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance so that additional configuration calls can be chained
+ /// if the given member was ignored, otherwise.
+ ///
+ new IConventionComplexTypeBuilder? Ignore(string memberName, bool fromDataAnnotation = false);
+
+ ///
+ /// Configures the to be used for this complex type.
+ /// This strategy indicates how the context detects changes to properties for an instance of the complex type.
+ ///
+ ///
+ /// The change tracking strategy to be used.
+ /// to reset to default.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the was set,
+ /// otherwise.
+ ///
+ new IConventionComplexTypeBuilder? HasChangeTrackingStrategy(
+ ChangeTrackingStrategy? changeTrackingStrategy,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the to use for all properties of this complex type.
+ ///
+ ///
+ /// The to use for properties of this complex type.
+ /// to reset to default.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ ///
+ /// The same builder instance if the was set,
+ /// otherwise.
+ ///
+ new IConventionComplexTypeBuilder? UsePropertyAccessMode(
+ PropertyAccessMode? propertyAccessMode,
+ bool fromDataAnnotation = false);
+}
diff --git a/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs b/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs
index da8fa4ea64a..97310be44a7 100644
--- a/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs
@@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
///
/// See Model building conventions for more information and examples.
///
-public interface IConventionEntityTypeBuilder : IConventionAnnotatableBuilder
+public interface IConventionEntityTypeBuilder : IConventionTypeBaseBuilder
{
///
/// Gets the entity type being configured.
@@ -23,141 +23,92 @@ public interface IConventionEntityTypeBuilder : IConventionAnnotatableBuilder
new IConventionEntityType Metadata { get; }
///
- /// Sets the base type of this entity type in an inheritance hierarchy.
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
///
- /// The base entity type or to indicate no base type.
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
/// Indicates whether the configuration was specified using a data annotation.
///
- /// The same builder instance if the base type was configured,
- /// otherwise.
+ /// An to continue configuration if the annotation was set, otherwise.
///
- IConventionEntityTypeBuilder? HasBaseType(
- IConventionEntityType? baseEntityType,
- bool fromDataAnnotation = false);
+ new IConventionEntityTypeBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
///
- /// Returns a value indicating whether the given type can be set as the base type of this entity type.
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
///
- /// The base entity type or to indicate no base type.
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
/// Indicates whether the configuration was specified using a data annotation.
- /// if the given type can be set as the base type of this entity type.
- bool CanSetBaseType(IConventionEntityType? baseEntityType, bool fromDataAnnotation = false);
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionEntityTypeBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
///
- /// Returns an object that can be used to configure the property with the given name.
- /// If no matching property exists, then a new property will be added.
+ /// Removes the annotation with the given name from this object.
///
- /// The type of value the property will hold.
- /// The name of the property to be configured.
- /// Indicates whether the type configuration source should be set.
+ /// The name of the annotation to remove.
/// Indicates whether the configuration was specified using a data annotation.
///
- /// An object that can be used to configure the property if it exists on the entity type,
- /// otherwise.
+ /// An to continue configuration if the annotation was set, otherwise.
///
- IConventionPropertyBuilder? Property(
- Type propertyType,
- string propertyName,
- bool setTypeConfigurationSource = true,
- bool fromDataAnnotation = false);
+ new IConventionEntityTypeBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
///
- /// Returns an object that can be used to configure the property with the given member info.
- /// If no matching property exists, then a new property will be added.
+ /// Sets the base type of this entity type in an inheritance hierarchy.
///
- /// The or of the property.
+ /// The base entity type or to indicate no base type.
/// Indicates whether the configuration was specified using a data annotation.
///
- /// An object that can be used to configure the property if it exists on the entity type,
+ /// The same builder instance if the base type was configured,
/// otherwise.
///
- IConventionPropertyBuilder? Property(MemberInfo memberInfo, bool fromDataAnnotation = false);
+ IConventionEntityTypeBuilder? HasBaseType(
+ IConventionEntityType? baseEntityType,
+ bool fromDataAnnotation = false);
///
- /// Returns a value indicating whether the given property can be added to this entity type.
+ /// Returns a value indicating whether the given type can be set as the base type of this entity type.
///
- /// The type of value the property will hold.
- /// The name of the property to be configured.
+ /// The base entity type or to indicate no base type.
/// Indicates whether the configuration was specified using a data annotation.
- /// if the property can be added.
- bool CanHaveProperty(
- Type? propertyType,
- string propertyName,
- bool fromDataAnnotation = false);
+ /// if the given type can be set as the base type of this entity type.
+ bool CanSetBaseType(IConventionEntityType? baseEntityType, bool fromDataAnnotation = false);
///
- /// Returns a value indicating whether the given property can be added to this entity type.
+ /// Removes properties in the given list if they are not part of any metadata object.
///
- /// The or of the property.
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the property can be added.
- bool CanHaveProperty(MemberInfo memberInfo, bool fromDataAnnotation = false);
+ /// The properties to remove.
+ new IConventionEntityTypeBuilder RemoveUnusedImplicitProperties(IReadOnlyList properties);
///
- /// Returns an object that can be used to configure the indexer property with the given name.
- /// If no matching property exists, then a new property will be added.
+ /// Removes a property from this entity type.
///
- /// The type of value the property will hold.
- /// The name of the property to be configured.
+ /// The property to be removed.
/// Indicates whether the configuration was specified using a data annotation.
///
- /// An object that can be used to configure the property if it exists on the entity type,
+ /// The same builder instance if the property was removed,
/// otherwise.
///
- IConventionPropertyBuilder? IndexerProperty(
- Type propertyType,
- string propertyName,
- bool fromDataAnnotation = false);
+ new IConventionEntityTypeBuilder? HasNoProperty(IConventionProperty property, bool fromDataAnnotation = false);
///
- /// Returns a value indicating whether the given indexer property can be added to this entity type.
+ /// Removes a complex property from this entity type.
///
- /// The type of value the property will hold.
- /// The name of the property to be configured.
+ /// The complex property to be removed.
/// Indicates whether the configuration was specified using a data annotation.
- /// if the property can be added.
- bool CanHaveIndexerProperty(
- Type propertyType,
- string propertyName,
- bool fromDataAnnotation = false);
-
- ///
- /// Creates a property with a name that's different from any existing properties.
- ///
- /// The desired property name.
- /// The type of value the property will hold.
- /// A value indicating whether the property is required.
///
- /// An object that can be used to configure the property if it exists on the entity type,
+ /// The same builder instance if the complex property was removed,
/// otherwise.
///
- IConventionPropertyBuilder? CreateUniqueProperty(Type propertyType, string basePropertyName, bool required);
-
- ///
- /// Returns the existing properties with the given names or creates them if matching CLR members are found.
- ///
- /// The names of the properties.
- /// Indicates whether the configuration was specified using a data annotation.
- /// A list of properties if they exist on the entity type, otherwise.
- IReadOnlyList? GetOrCreateProperties(
- IReadOnlyList? propertyNames,
- bool fromDataAnnotation = false);
-
- ///
- /// Returns the existing properties matching the given members or creates them.
- ///
- /// The type members.
- /// Indicates whether the configuration was specified using a data annotation.
- /// A list of properties if they exist on the entity type, otherwise.
- IReadOnlyList? GetOrCreateProperties(
- IEnumerable? memberInfos,
- bool fromDataAnnotation = false);
-
- ///
- /// Removes properties in the given list if they are not part of any metadata object.
- ///
- /// The properties to remove.
- IConventionEntityTypeBuilder RemoveUnusedImplicitProperties(IReadOnlyList properties);
+ new IConventionEntityTypeBuilder? HasNoComplexProperty(IConventionComplexProperty complexProperty, bool fromDataAnnotation = false);
///
/// Returns an object that can be used to configure the service property with the given member info.
@@ -198,41 +149,35 @@ bool CanHaveIndexerProperty(
bool CanHaveServiceProperty(MemberInfo memberInfo, bool fromDataAnnotation = false);
///
- /// Indicates whether the given member name is ignored for the given configuration source.
+ /// Removes a service property from this entity type.
///
- /// The name of the member that might be ignored.
+ /// The service property to be removed.
/// Indicates whether the configuration was specified using a data annotation.
///
- /// if the entity type contains a member with the given name,
- /// the given member name hasn't been ignored or it was ignored using a lower configuration source;
- /// otherwise.
+ /// The same builder instance if the service property was removed,
+ /// otherwise.
///
- bool IsIgnored(string memberName, bool fromDataAnnotation = false);
+ IConventionEntityTypeBuilder? HasNoServiceProperty(IConventionServiceProperty serviceProperty, bool fromDataAnnotation = false);
///
- /// Excludes the given property from the entity type and prevents conventions from adding a matching property
- /// or navigation to the type.
+ /// Returns a value indicating whether the service property can be removed from this entity type.
///
- /// The name of the member to be removed.
+ /// The service property to be removed.
/// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same instance so that additional configuration calls can be chained
- /// if the given member was ignored, otherwise.
- ///
- IConventionEntityTypeBuilder? Ignore(string memberName, bool fromDataAnnotation = false);
+ /// if the service property can be removed from this entity type.
+ bool CanRemoveServiceProperty(IConventionServiceProperty serviceProperty, bool fromDataAnnotation = false);
///
- /// Returns a value indicating whether the given member name can be ignored from the given configuration source.
+ /// Excludes the given property from the entity type and prevents conventions from adding a matching property
+ /// or navigation to the type.
///
- /// The member name to be removed from the entity type.
+ /// The name of the member to be removed.
/// Indicates whether the configuration was specified using a data annotation.
- /// if the given member name can be ignored.
///
- /// if the entity type contains a member with the given name
- /// that was configured using a higher configuration source;
- /// otherwise.
+ /// The same builder instance so that additional configuration calls can be chained
+ /// if the given member was ignored, otherwise.
///
- bool CanIgnore(string memberName, bool fromDataAnnotation = false);
+ new IConventionEntityTypeBuilder? Ignore(string memberName, bool fromDataAnnotation = false);
///
/// Sets the properties that make up the primary key for this entity type.
@@ -743,25 +688,6 @@ bool CanHaveIndexerProperty(
/// if the foreign key can be removed from this entity type.
bool CanRemoveRelationship(IConventionForeignKey foreignKey, bool fromDataAnnotation = false);
- ///
- /// Removes a skip navigation from this entity type.
- ///
- /// The skip navigation to be removed.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the skip navigation was removed,
- /// otherwise.
- ///
- IConventionEntityTypeBuilder? HasNoSkipNavigation(IConventionSkipNavigation skipNavigation, bool fromDataAnnotation = false);
-
- ///
- /// Returns a value indicating whether the skip navigation can be removed from this entity type.
- ///
- /// The skip navigation to be removed.
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the skip navigation can be removed from this entity type.
- bool CanRemoveSkipNavigation(IConventionSkipNavigation skipNavigation, bool fromDataAnnotation = false);
-
///
/// Returns a value indicating whether the given navigation can be added to this entity type.
///
@@ -780,6 +706,25 @@ bool CanHaveIndexerProperty(
bool CanHaveNavigation(MemberInfo navigation, bool fromDataAnnotation = false)
=> CanHaveNavigation(navigation.Name, navigation.GetMemberType(), fromDataAnnotation);
+ ///
+ /// Removes a navigation from this entity type.
+ ///
+ /// The navigation to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the navigation was removed,
+ /// otherwise.
+ ///
+ IConventionEntityTypeBuilder? HasNoNavigation(IConventionNavigation navigation, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the navigation can be removed from this entity type.
+ ///
+ /// The navigation to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the navigation can be removed from this entity type.
+ bool CanRemoveNavigation(IConventionNavigation navigation, bool fromDataAnnotation = false);
+
///
/// Returns a value indicating whether the given skip navigation can be added to this entity type.
///
@@ -850,6 +795,7 @@ bool CanHaveSkipNavigation(MemberInfo navigation, bool fromDataAnnotation = fals
///
/// The navigation property name.
/// The entity type that the navigation targets.
+ /// The navigation type.
/// Whether the navigation property is a collection property.
///
/// Whether the navigation property is defined on the dependent side of the underlying foreign key.
@@ -862,10 +808,30 @@ bool CanHaveSkipNavigation(MemberInfo navigation, bool fromDataAnnotation = fals
IConventionSkipNavigationBuilder? HasSkipNavigation(
string navigationName,
IConventionEntityType targetEntityType,
+ Type? navigationType = null,
bool? collection = null,
bool? onDependent = null,
bool fromDataAnnotation = false);
+ ///
+ /// Removes a skip navigation from this entity type.
+ ///
+ /// The skip navigation to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the skip navigation was removed,
+ /// otherwise.
+ ///
+ IConventionEntityTypeBuilder? HasNoSkipNavigation(IConventionSkipNavigation skipNavigation, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the skip navigation can be removed from this entity type.
+ ///
+ /// The skip navigation to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the skip navigation can be removed from this entity type.
+ bool CanRemoveSkipNavigation(IConventionSkipNavigation skipNavigation, bool fromDataAnnotation = false);
+
///
/// Configures a database trigger when targeting a relational database.
///
@@ -945,21 +911,10 @@ bool CanHaveTrigger(
/// The same builder instance if the was set,
/// otherwise.
///
- IConventionEntityTypeBuilder? HasChangeTrackingStrategy(
+ new IConventionEntityTypeBuilder? HasChangeTrackingStrategy(
ChangeTrackingStrategy? changeTrackingStrategy,
bool fromDataAnnotation = false);
- ///
- /// Returns a value indicating whether the given change tracking strategy can be set from the current configuration source.
- ///
- ///
- /// The change tracking strategy to be used.
- /// to reset to default.
- ///
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the given change tracking strategy can be set.
- bool CanSetChangeTrackingStrategy(ChangeTrackingStrategy? changeTrackingStrategy, bool fromDataAnnotation = false);
-
///
/// Sets the to use for all properties of this entity type.
///
@@ -973,21 +928,10 @@ bool CanHaveTrigger(
/// The same builder instance if the was set,
/// otherwise.
///
- IConventionEntityTypeBuilder? UsePropertyAccessMode(
+ new IConventionEntityTypeBuilder? UsePropertyAccessMode(
PropertyAccessMode? propertyAccessMode,
bool fromDataAnnotation = false);
- ///
- /// Returns a value indicating whether the given can be set from the current configuration source.
- ///
- ///
- /// The to use for properties of this model.
- /// to reset to default.
- ///
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the given can be set.
- bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
-
///
/// Configures the discriminator property used to identify which entity type each row in a table represents
/// when an inheritance hierarchy is mapped to a single table in a relational database.
diff --git a/src/EFCore/Metadata/Builders/IConventionForeignKeyBuilder.cs b/src/EFCore/Metadata/Builders/IConventionForeignKeyBuilder.cs
index 0e5778ba2ed..b3c2a2747b8 100644
--- a/src/EFCore/Metadata/Builders/IConventionForeignKeyBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionForeignKeyBuilder.cs
@@ -22,6 +22,45 @@ public interface IConventionForeignKeyBuilder : IConventionAnnotatableBuilder
///
new IConventionForeignKey Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionForeignKeyBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionForeignKeyBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionForeignKeyBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Configures which entity types participate in this relationship.
/// By calling this method the principal and dependent types can be switched or the relationship could
diff --git a/src/EFCore/Metadata/Builders/IConventionIndexBuilder.cs b/src/EFCore/Metadata/Builders/IConventionIndexBuilder.cs
index 93d7dafa6f8..142d6d69774 100644
--- a/src/EFCore/Metadata/Builders/IConventionIndexBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionIndexBuilder.cs
@@ -22,6 +22,45 @@ public interface IConventionIndexBuilder : IConventionAnnotatableBuilder
///
new IConventionIndex Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionIndexBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionIndexBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionIndexBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Configures whether this index is unique (i.e. each set of values must be unique).
///
diff --git a/src/EFCore/Metadata/Builders/IConventionKeyBuilder.cs b/src/EFCore/Metadata/Builders/IConventionKeyBuilder.cs
index eef66af6481..104d3997d90 100644
--- a/src/EFCore/Metadata/Builders/IConventionKeyBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionKeyBuilder.cs
@@ -21,4 +21,43 @@ public interface IConventionKeyBuilder : IConventionAnnotatableBuilder
/// Gets the key being configured.
///
new IConventionKey Metadata { get; }
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionKeyBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionKeyBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionKeyBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
}
diff --git a/src/EFCore/Metadata/Builders/IConventionModelBuilder.cs b/src/EFCore/Metadata/Builders/IConventionModelBuilder.cs
index a8fcea58cb0..e0076e164fb 100644
--- a/src/EFCore/Metadata/Builders/IConventionModelBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionModelBuilder.cs
@@ -24,6 +24,45 @@ public interface IConventionModelBuilder : IConventionAnnotatableBuilder
///
new IConventionModel Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionModelBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionModelBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionModelBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Returns an object that can be used to configure a given entity type in the model.
/// If an entity type with the provided name is not already part of the model,
@@ -182,6 +221,38 @@ public interface IConventionModelBuilder : IConventionAnnotatableBuilder
///
IConventionModelBuilder? Ignore(string typeName, bool fromDataAnnotation = false);
+ ///
+ /// Returns a value indicating whether the given entity type can be added to the model.
+ ///
+ /// The name of the entity type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the entity type can be added.
+ bool CanHaveEntity(
+ string name,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given entity type can be added to the model.
+ ///
+ /// The type of the entity type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the entity type can be added.
+ bool CanHaveEntity(
+ [DynamicallyAccessedMembers(IEntityType.DynamicallyAccessedMemberTypes)] Type type,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given entity type can be added to the model.
+ ///
+ /// The name of the entity type.
+ /// The type of the entity type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the entity type can be added.
+ bool CanHaveSharedTypeEntity(
+ string name,
+ [DynamicallyAccessedMembers(IEntityType.DynamicallyAccessedMemberTypes)] Type? type,
+ bool fromDataAnnotation = false);
+
///
/// Removes the given entity type from the model.
///
@@ -193,7 +264,15 @@ public interface IConventionModelBuilder : IConventionAnnotatableBuilder
IConventionModelBuilder? HasNoEntityType(IConventionEntityType entityType, bool fromDataAnnotation = false);
///
- /// Returns a value indicating whether the given entity type can be ignored from the current configuration source
+ /// Returns a value indicating whether the entity type can be removed from the model.
+ ///
+ /// The entity type to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the navigation can be removed from this entity type.
+ bool CanRemoveEntity(IConventionEntityType entityType, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given entity type can be ignored from the current configuration source.
///
/// The entity type to be removed from the model.
/// Indicates whether the configuration was specified using a data annotation.
diff --git a/src/EFCore/Metadata/Builders/IConventionNavigationBuilder.cs b/src/EFCore/Metadata/Builders/IConventionNavigationBuilder.cs
index cdcb4fe0fad..82c073afebc 100644
--- a/src/EFCore/Metadata/Builders/IConventionNavigationBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionNavigationBuilder.cs
@@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
///
/// See Model building conventions for more information and examples.
///
-public interface IConventionNavigationBuilder : IConventionPropertyBaseBuilder
+public interface IConventionNavigationBuilder : IConventionPropertyBaseBuilder
{
///
/// Gets the navigation being configured.
@@ -23,37 +23,15 @@ public interface IConventionNavigationBuilder : IConventionPropertyBaseBuilder
new IConventionNavigation Metadata { get; }
///
- /// Sets the backing field to use for this navigation.
- ///
- /// The field name.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionNavigationBuilder? HasField(string? fieldName, bool fromDataAnnotation = false);
-
- ///
- /// Sets the backing field to use for this navigation.
- ///
- /// The field.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionNavigationBuilder? HasField(FieldInfo? fieldInfo, bool fromDataAnnotation = false);
-
- ///
- /// Sets the to use for this navigation.
+ /// Configures this navigation to be automatically included in a query.
///
- /// The to use for this navigation.
+ /// A value indicating whether the navigation should be automatically included.
/// Indicates whether the configuration was specified using a data annotation.
///
/// The same builder instance if the configuration was applied,
/// otherwise.
///
- new IConventionNavigationBuilder? UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
+ IConventionNavigationBuilder? AutoInclude(bool? autoInclude, bool fromDataAnnotation = false);
///
/// Returns a value indicating whether this navigation can be configured to be automatically included in a query
@@ -65,15 +43,15 @@ public interface IConventionNavigationBuilder : IConventionPropertyBaseBuilder
bool CanSetAutoInclude(bool? autoInclude, bool fromDataAnnotation = false);
///
- /// Configures this navigation to be automatically included in a query.
+ /// Configures this navigation to be enabled for lazy-loading.
///
- /// A value indicating whether the navigation should be automatically included.
+ /// A value indicating whether the navigation should be enabled for lazy-loading.
/// Indicates whether the configuration was specified using a data annotation.
///
/// The same builder instance if the configuration was applied,
/// otherwise.
///
- IConventionNavigationBuilder? AutoInclude(bool? autoInclude, bool fromDataAnnotation = false);
+ IConventionNavigationBuilder? EnableLazyLoading(bool? lazyLoadingEnabled, bool fromDataAnnotation = false);
///
/// Returns a value indicating whether this navigation can be configured to enable lazy-loading
@@ -85,15 +63,17 @@ public interface IConventionNavigationBuilder : IConventionPropertyBaseBuilder
bool CanSetLazyLoadingEnabled(bool? lazyLoadingEnabled, bool fromDataAnnotation = false);
///
- /// Configures this navigation to be enabled for lazy-loading.
+ /// Configures whether this navigation is required.
///
- /// A value indicating whether the navigation should be enabled for lazy-loading.
+ ///
+ /// A value indicating whether this is a required navigation.
+ /// to reset to default.
+ ///
/// Indicates whether the configuration was specified using a data annotation.
///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
+ /// The same builder instance if the requiredness was configured, otherwise.
///
- IConventionNavigationBuilder? EnableLazyLoading(bool? lazyLoadingEnabled, bool fromDataAnnotation = false);
+ IConventionNavigationBuilder? IsRequired(bool? required, bool fromDataAnnotation = false);
///
/// Returns a value indicating whether this navigation requiredness can be configured
@@ -103,17 +83,4 @@ public interface IConventionNavigationBuilder : IConventionPropertyBaseBuilder
/// Indicates whether the configuration was specified using a data annotation.
/// if requiredness can be set for this navigation.
bool CanSetIsRequired(bool? required, bool fromDataAnnotation = false);
-
- ///
- /// Configures whether this navigation is required.
- ///
- ///
- /// A value indicating whether this is a required navigation.
- /// to reset to default.
- ///
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the requiredness was configured, otherwise.
- ///
- IConventionNavigationBuilder? IsRequired(bool? required, bool fromDataAnnotation = false);
}
diff --git a/src/EFCore/Metadata/Builders/IConventionPropertyBaseBuilder.cs b/src/EFCore/Metadata/Builders/IConventionPropertyBaseBuilder.cs
index ebff66d4ea3..2bdd732ab94 100644
--- a/src/EFCore/Metadata/Builders/IConventionPropertyBaseBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionPropertyBaseBuilder.cs
@@ -15,13 +15,53 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
///
/// See Model building conventions for more information and examples.
///
-public interface IConventionPropertyBaseBuilder : IConventionAnnotatableBuilder
+public interface IConventionPropertyBaseBuilder : IConventionAnnotatableBuilder
+ where TBuilder : IConventionPropertyBaseBuilder
{
///
/// Gets the property-like object being configured.
///
new IConventionPropertyBase Metadata { get; }
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder to continue configuration if the annotation was set, otherwise.
+ ///
+ new TBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new TBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder to continue configuration if the annotation was set, otherwise.
+ ///
+ new TBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
///
/// Sets the backing field to use for this property-like object.
///
@@ -31,7 +71,7 @@ public interface IConventionPropertyBaseBuilder : IConventionAnnotatableBuilder
/// The same builder instance if the configuration was applied,
/// otherwise.
///
- IConventionPropertyBaseBuilder? HasField(string? fieldName, bool fromDataAnnotation = false);
+ TBuilder? HasField(string? fieldName, bool fromDataAnnotation = false);
///
/// Sets the backing field to use for this property-like object.
@@ -42,7 +82,7 @@ public interface IConventionPropertyBaseBuilder : IConventionAnnotatableBuilder
/// The same builder instance if the configuration was applied,
/// otherwise.
///
- IConventionPropertyBaseBuilder? HasField(FieldInfo? fieldInfo, bool fromDataAnnotation = false);
+ TBuilder? HasField(FieldInfo? fieldInfo, bool fromDataAnnotation = false);
///
/// Returns a value indicating whether the backing field can be set for this property-like object
@@ -71,7 +111,7 @@ public interface IConventionPropertyBaseBuilder : IConventionAnnotatableBuilder
/// The same builder instance if the configuration was applied,
/// otherwise.
///
- IConventionPropertyBaseBuilder? UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
+ TBuilder? UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
///
/// Returns a value indicating whether the can be set for this property-like object
diff --git a/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs b/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs
index 8868fe52f08..7254760366e 100644
--- a/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs
@@ -17,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
///
/// See Model building conventions for more information and examples.
///
-public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder
+public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder
{
///
/// Gets the property being configured.
@@ -107,37 +107,22 @@ public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder
bool CanSetIsConcurrencyToken(bool? concurrencyToken, bool fromDataAnnotation = false);
///
- /// Sets the backing field to use for this property.
- ///
- /// The field name.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionPropertyBuilder? HasField(string? fieldName, bool fromDataAnnotation = false);
-
- ///
- /// Sets the backing field to use for this property.
+ /// 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 field.
+ /// The sentinel value.
/// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionPropertyBuilder? HasField(FieldInfo? fieldInfo, bool fromDataAnnotation = false);
+ /// The same builder instance if the configuration was applied, otherwise.
+ IConventionPropertyBuilder? HasSentinel(object? sentinel, bool fromDataAnnotation = false);
///
- /// Sets the to use for this property.
+ /// Returns a value indicating whether the sentinel can be set for this property from the current configuration source.
///
- /// The to use for this property.
+ /// The sentinel value.
/// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionPropertyBuilder? UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
+ /// if the sentinel can be set for this property.
+ bool CanSetSentinel(object? sentinel, bool fromDataAnnotation = false);
///
/// Configures the maximum length of data that can be stored in this property.
@@ -161,24 +146,6 @@ public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder
/// if the maximum length of data allowed can be set for this property.
bool CanSetMaxLength(int? maxLength, bool fromDataAnnotation = false);
- ///
- /// 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.
- /// Indicates whether the configuration was specified using a data annotation.
- /// The same builder instance if the configuration was applied, otherwise.
- IConventionPropertyBuilder? HasSentinel(object? sentinel, bool fromDataAnnotation = false);
-
- ///
- /// Returns a value indicating whether the sentinel can be set for this property from the current configuration source.
- ///
- /// The sentinel value.
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the sentinel can be set for this property.
- bool CanSetSentinel(object? sentinel, bool fromDataAnnotation = false);
-
///
/// Configures whether the property as capable of persisting unicode characters.
///
@@ -318,7 +285,7 @@ public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder
/// otherwise.
///
IConventionPropertyBuilder? HasValueGenerator(
- Func? factory,
+ Func? factory,
bool fromDataAnnotation = false);
///
@@ -349,7 +316,7 @@ public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder
/// if the can be configured for this property.
///
bool CanSetValueGenerator(
- Func? factory,
+ Func? factory,
bool fromDataAnnotation = false);
///
diff --git a/src/EFCore/Metadata/Builders/IConventionServicePropertyBuilder.cs b/src/EFCore/Metadata/Builders/IConventionServicePropertyBuilder.cs
index 957949626d1..2fbf8a4284d 100644
--- a/src/EFCore/Metadata/Builders/IConventionServicePropertyBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionServicePropertyBuilder.cs
@@ -15,48 +15,13 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
///
/// See Model building conventions for more information and examples.
///
-public interface IConventionServicePropertyBuilder : IConventionPropertyBaseBuilder
+public interface IConventionServicePropertyBuilder : IConventionPropertyBaseBuilder
{
///
/// Gets the service property being configured.
///
new IConventionServiceProperty Metadata { get; }
- ///
- /// Sets the backing field to use for this property.
- ///
- /// The field name.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionServicePropertyBuilder? HasField(string? fieldName, bool fromDataAnnotation = false);
-
- ///
- /// Sets the backing field to use for this property.
- ///
- /// The field.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionServicePropertyBuilder? HasField(FieldInfo? fieldInfo, bool fromDataAnnotation = false);
-
- ///
- /// Sets the to use for this property.
- ///
- /// The to use for this property.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionServicePropertyBuilder? UsePropertyAccessMode(
- PropertyAccessMode? propertyAccessMode,
- bool fromDataAnnotation = false);
-
///
/// Sets the for this property.
///
diff --git a/src/EFCore/Metadata/Builders/IConventionSkipNavigationBuilder.cs b/src/EFCore/Metadata/Builders/IConventionSkipNavigationBuilder.cs
index b1a1f8d818b..1c189bc815f 100644
--- a/src/EFCore/Metadata/Builders/IConventionSkipNavigationBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionSkipNavigationBuilder.cs
@@ -15,48 +15,13 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
///
/// See Model building conventions for more information and examples.
///
-public interface IConventionSkipNavigationBuilder : IConventionPropertyBaseBuilder
+public interface IConventionSkipNavigationBuilder : IConventionPropertyBaseBuilder
{
///
/// Gets the navigation property being configured.
///
new IConventionSkipNavigation Metadata { get; }
- ///
- /// Sets the backing field to use for this navigation.
- ///
- /// The field name.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionSkipNavigationBuilder? HasField(string? fieldName, bool fromDataAnnotation = false);
-
- ///
- /// Sets the backing field to use for this navigation.
- ///
- /// The field.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionSkipNavigationBuilder? HasField(FieldInfo? fieldInfo, bool fromDataAnnotation = false);
-
- ///
- /// Sets the to use for this navigation.
- ///
- /// The to use for this navigation.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the configuration was applied,
- /// otherwise.
- ///
- new IConventionSkipNavigationBuilder? UsePropertyAccessMode(
- PropertyAccessMode? propertyAccessMode,
- bool fromDataAnnotation = false);
-
///
/// Sets the foreign key.
///
diff --git a/src/EFCore/Metadata/Builders/IConventionTriggerBuilder.cs b/src/EFCore/Metadata/Builders/IConventionTriggerBuilder.cs
index 49d79bf56f6..aeea9cbfe1d 100644
--- a/src/EFCore/Metadata/Builders/IConventionTriggerBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionTriggerBuilder.cs
@@ -15,4 +15,43 @@ public interface IConventionTriggerBuilder : IConventionAnnotatableBuilder
/// The trigger being configured.
///
new IConventionTrigger Metadata { get; }
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionTriggerBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionTriggerBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionTriggerBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
}
diff --git a/src/EFCore/Metadata/Builders/IConventionTypeBaseBuilder.cs b/src/EFCore/Metadata/Builders/IConventionTypeBaseBuilder.cs
new file mode 100644
index 00000000000..26ce5a70949
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/IConventionTypeBaseBuilder.cs
@@ -0,0 +1,402 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+///
+/// Provides a simple API surface for configuring an from conventions.
+///
+///
+/// This interface is typically used by database providers (and other extensions). It is generally
+/// not used in application code.
+///
+///
+///
+/// See Model building conventions for more information and examples.
+///
+public interface IConventionTypeBaseBuilder : IConventionAnnotatableBuilder
+{
+ ///
+ /// Gets the type-like object being configured.
+ ///
+ new IConventionTypeBase Metadata { get; }
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionTypeBaseBuilder? HasAnnotation(string name, object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the annotation stored under the given name. Overwrites the existing annotation if an
+ /// annotation with the specified name already exists with same or lower .
+ /// Removes the annotation if value is specified.
+ ///
+ /// The name of the annotation to be set.
+ /// The value to be stored in the annotation. to remove the annotations.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set or removed,
+ /// otherwise.
+ ///
+ new IConventionTypeBaseBuilder? HasNonNullAnnotation(
+ string name,
+ object? value,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the annotation with the given name from this object.
+ ///
+ /// The name of the annotation to remove.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An to continue configuration if the annotation was set, otherwise.
+ ///
+ new IConventionTypeBaseBuilder? HasNoAnnotation(string name, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns an object that can be used to configure the property with the given name.
+ /// If no matching property exists, then a new property will be added.
+ ///
+ /// The type of value the property will hold.
+ /// The name of the property to be configured.
+ /// Indicates whether the type configuration source should be set.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An object that can be used to configure the property if it exists on the type,
+ /// otherwise.
+ ///
+ IConventionPropertyBuilder? Property(
+ Type propertyType,
+ string propertyName,
+ bool setTypeConfigurationSource = true,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns an object that can be used to configure the property with the given member info.
+ /// If no matching property exists, then a new property will be added.
+ ///
+ /// The or of the property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An object that can be used to configure the property if it exists on the type,
+ /// otherwise.
+ ///
+ IConventionPropertyBuilder? Property(MemberInfo memberInfo, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given property can be added to this type.
+ ///
+ /// The type of value the property will hold.
+ /// The name of the property to be configured.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the property can be added.
+ bool CanHaveProperty(
+ Type? propertyType,
+ string propertyName,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given property can be added to this type.
+ ///
+ /// The or of the property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the property can be added.
+ bool CanHaveProperty(MemberInfo memberInfo, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns an object that can be used to configure the indexer property with the given name.
+ /// If no matching property exists, then a new property will be added.
+ ///
+ /// The type of value the property will hold.
+ /// The name of the property to be configured.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An object that can be used to configure the property if it exists on the type,
+ /// otherwise.
+ ///
+ IConventionPropertyBuilder? IndexerProperty(
+ Type propertyType,
+ string propertyName,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given indexer property can be added to this type.
+ ///
+ /// The type of value the property will hold.
+ /// The name of the property to be configured.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the property can be added.
+ bool CanHaveIndexerProperty(
+ Type propertyType,
+ string propertyName,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Creates a property with a name that's different from any existing properties.
+ ///
+ /// The desired property name.
+ /// The type of value the property will hold.
+ /// A value indicating whether the property is required.
+ ///
+ /// An object that can be used to configure the property if it exists on the type,
+ /// otherwise.
+ ///
+ IConventionPropertyBuilder? CreateUniqueProperty(Type propertyType, string basePropertyName, bool required);
+
+ ///
+ /// Returns the existing properties with the given names or creates them if matching CLR members are found.
+ ///
+ /// The names of the properties.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// A list of properties if they exist on the type, otherwise.
+ IReadOnlyList? GetOrCreateProperties(
+ IReadOnlyList? propertyNames,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns the existing properties matching the given members or creates them.
+ ///
+ /// The type members.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// A list of properties if they exist on the type, otherwise.
+ IReadOnlyList? GetOrCreateProperties(
+ IEnumerable? memberInfos,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes properties in the given list if they are not part of any metadata object.
+ ///
+ /// The properties to remove.
+ IConventionTypeBaseBuilder RemoveUnusedImplicitProperties(IReadOnlyList properties);
+
+ ///
+ /// Removes a property from this type.
+ ///
+ /// The property to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the property was removed,
+ /// otherwise.
+ ///
+ IConventionTypeBaseBuilder? HasNoProperty(IConventionProperty property, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the property can be removed from this type.
+ ///
+ /// The property to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the property can be removed from this type.
+ bool CanRemoveProperty(IConventionProperty property, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns an object that can be used to configure the complex property with the given name.
+ /// If no matching property exists, then a new property will be added.
+ ///
+ /// The type of value the property will hold.
+ /// The name of the property to be configured.
+ /// The target complex type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An object that can be used to configure the property if it exists on the type,
+ /// otherwise.
+ ///
+ IConventionComplexPropertyBuilder? ComplexProperty(
+ Type propertyType,
+ string propertyName,
+ Type? complexType = null,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns an object that can be used to configure the complex property with the given member info.
+ /// If no matching property exists, then a new property will be added.
+ ///
+ /// The or of the property.
+ /// The target complex type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An object that can be used to configure the property if it exists on the type,
+ /// otherwise.
+ ///
+ IConventionComplexPropertyBuilder? ComplexProperty(
+ MemberInfo memberInfo,
+ Type? complexType = null,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given complex property can be added to this type.
+ ///
+ /// The type of value the property will hold.
+ /// The name of the property to be configured.
+ /// The target complex type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the property can be added.
+ bool CanHaveComplexProperty(
+ Type? propertyType,
+ string propertyName,
+ Type? complexType = null,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given complex property can be added to this type.
+ ///
+ /// The or of the property.
+ /// The target complex type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the property can be added.
+ bool CanHaveComplexProperty(
+ MemberInfo memberInfo,
+ Type? complexType = null,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns an object that can be used to configure the complex indexer property with the given name.
+ /// If no matching property exists, then a new property will be added.
+ ///
+ /// The type of value the property will hold.
+ /// The name of the property to be configured.
+ /// The target complex type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// An object that can be used to configure the property if it exists on the type,
+ /// otherwise.
+ ///
+ IConventionComplexPropertyBuilder? ComplexIndexerProperty(
+ Type propertyType,
+ string propertyName,
+ Type? complexType,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given complex indexer property can be added to this type.
+ ///
+ /// The type of value the property will hold.
+ /// The name of the property to be configured.
+ /// The target complex type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the property can be added.
+ bool CanHaveComplexIndexerProperty(
+ Type propertyType,
+ string propertyName,
+ Type? complexType,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Removes a complex property from this type.
+ ///
+ /// The complex property to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the complex property was removed,
+ /// otherwise.
+ ///
+ IConventionTypeBaseBuilder? HasNoComplexProperty(IConventionComplexProperty complexProperty, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the complex property can be removed from this type.
+ ///
+ /// The complex property to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the complex property can be removed from this type.
+ bool CanRemoveComplexProperty(IConventionComplexProperty complexProperty, bool fromDataAnnotation = false);
+
+ ///
+ /// Indicates whether the given member name is ignored for the given configuration source.
+ ///
+ /// The name of the member that might be ignored.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// if the complex type contains a member with the given name,
+ /// the given member name hasn't been ignored or it was ignored using a lower configuration source;
+ /// otherwise.
+ ///
+ bool IsIgnored(string memberName, bool fromDataAnnotation = false);
+
+ ///
+ /// Excludes the given property from the complex type and prevents conventions from adding a matching property
+ /// or navigation to the type.
+ ///
+ /// The name of the member to be removed.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance so that additional configuration calls can be chained
+ /// if the given member was ignored, otherwise.
+ ///
+ IConventionTypeBaseBuilder? Ignore(string memberName, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given member name can be ignored from the given configuration source.
+ ///
+ /// The member name to be removed from the complex type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the given member name can be ignored.
+ ///
+ /// if the complex type contains a member with the given name
+ /// that was configured using a higher configuration source;
+ /// otherwise.
+ ///
+ bool CanIgnore(string memberName, bool fromDataAnnotation = false);
+
+ ///
+ /// Configures the to be used for this type.
+ /// This strategy indicates how the context detects changes to properties for an instance of the type.
+ ///
+ ///
+ /// The change tracking strategy to be used.
+ /// to reset to default.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the was set,
+ /// otherwise.
+ ///
+ IConventionTypeBaseBuilder? HasChangeTrackingStrategy(
+ ChangeTrackingStrategy? changeTrackingStrategy,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given change tracking strategy can be set from the current configuration source.
+ ///
+ ///
+ /// The change tracking strategy to be used.
+ /// to reset to default.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the given change tracking strategy can be set.
+ bool CanSetChangeTrackingStrategy(ChangeTrackingStrategy? changeTrackingStrategy, bool fromDataAnnotation = false);
+
+ ///
+ /// Sets the to use for all properties of this type.
+ ///
+ ///
+ /// The to use for properties of this type.
+ /// to reset to default.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ ///
+ /// The same builder instance if the was set,
+ /// otherwise.
+ ///
+ IConventionTypeBaseBuilder? UsePropertyAccessMode(
+ PropertyAccessMode? propertyAccessMode,
+ bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the given can be set from the current configuration source.
+ ///
+ ///
+ /// The to use for properties of this model.
+ /// to reset to default.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the given can be set.
+ bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
+}
diff --git a/src/EFCore/Metadata/Builders/PropertyBuilder.cs b/src/EFCore/Metadata/Builders/PropertyBuilder.cs
index 4f9155f3c9d..1e8604eda01 100644
--- a/src/EFCore/Metadata/Builders/PropertyBuilder.cs
+++ b/src/EFCore/Metadata/Builders/PropertyBuilder.cs
@@ -250,7 +250,7 @@ public virtual PropertyBuilder HasValueGenerator(
///
/// A delegate that will be used to create value generator instances.
/// The same builder instance so that multiple configuration calls can be chained.
- public virtual PropertyBuilder HasValueGenerator(Func factory)
+ public virtual PropertyBuilder HasValueGenerator(Func factory)
{
Check.NotNull(factory, nameof(factory));
@@ -439,7 +439,7 @@ public virtual PropertyBuilder HasField(string fieldName)
///
/// 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
+ /// Properties are used for all other accesses. Calling this method will change that behavior
/// for this property as described in the enum.
///
///
diff --git a/src/EFCore/Metadata/Builders/PropertyBuilder`.cs b/src/EFCore/Metadata/Builders/PropertyBuilder`.cs
index a4264488fc0..19d1e0fbf6a 100644
--- a/src/EFCore/Metadata/Builders/PropertyBuilder`.cs
+++ b/src/EFCore/Metadata/Builders/PropertyBuilder`.cs
@@ -188,7 +188,7 @@ public PropertyBuilder(IMutableProperty property)
///
/// A delegate that will be used to create value generator instances.
/// The same builder instance so that multiple configuration calls can be chained.
- public new virtual PropertyBuilder HasValueGenerator(Func factory)
+ public new virtual PropertyBuilder HasValueGenerator(Func