diff --git a/src/EFCore.Relational/Metadata/Conventions/CheckConstraintConvention.cs b/src/EFCore.Relational/Metadata/Conventions/CheckConstraintConvention.cs
index d7f4451e502..9279af77a42 100644
--- a/src/EFCore.Relational/Metadata/Conventions/CheckConstraintConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/CheckConstraintConvention.cs
@@ -13,12 +13,12 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
///
/// A convention that ensures that the check constraints on the derived types are compatible with
- /// the check constraints on the base type.
+ /// the check constraints on the base type. And also ensures that the declaring type is current.
///
///
/// See Model building conventions for more information.
///
- public class CheckConstraintConvention : IEntityTypeBaseTypeChangedConvention
+ public class CheckConstraintConvention : IEntityTypeBaseTypeChangedConvention, IEntityTypeAddedConvention
{
///
/// Creates a new instance of .
@@ -43,6 +43,50 @@ public CheckConstraintConvention(
///
protected virtual RelationalConventionSetBuilderDependencies RelationalDependencies { get; }
+ ///
+ /// Called after an entity type is added to the model.
+ ///
+ /// The builder for the entity type.
+ /// Additional information associated with convention execution.
+ public virtual void ProcessEntityTypeAdded(IConventionEntityTypeBuilder entityTypeBuilder, IConventionContext context)
+ {
+ var entityType = entityTypeBuilder.Metadata;
+ if (!entityType.HasSharedClrType)
+ {
+ return;
+ }
+
+ List? constraintsToReattach = null;
+ foreach (var checkConstraint in entityType.GetCheckConstraints())
+ {
+ if (checkConstraint.EntityType == entityType)
+ {
+ continue;
+ }
+
+ if (constraintsToReattach == null)
+ {
+ constraintsToReattach = new();
+ }
+
+ constraintsToReattach.Add(checkConstraint);
+ }
+
+ if (constraintsToReattach == null)
+ {
+ return;
+ }
+
+ foreach (var checkConstraint in constraintsToReattach)
+ {
+ var removedCheckConstraint = entityType.RemoveCheckConstraint(checkConstraint.ModelName);
+ if (removedCheckConstraint != null)
+ {
+ CheckConstraint.Attach(entityType, removedCheckConstraint);
+ }
+ }
+ }
+
///
/// Called after the base type of an entity type changes.
///
diff --git a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs
index 59764963ed2..7e2a71208e5 100644
--- a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs
@@ -67,19 +67,23 @@ public override ConventionSet CreateConventionSet()
conventionSet.PropertyAddedConventions.Add(relationalColumnAttributeConvention);
conventionSet.PropertyAddedConventions.Add(relationalCommentAttributeConvention);
+ var checkConstraintConvention = new CheckConstraintConvention(Dependencies, RelationalDependencies);
var tableNameFromDbSetConvention = new TableNameFromDbSetConvention(Dependencies, RelationalDependencies);
conventionSet.EntityTypeAddedConventions.Add(new RelationalTableAttributeConvention(Dependencies, RelationalDependencies));
conventionSet.EntityTypeAddedConventions.Add(
new RelationalTableCommentAttributeConvention(Dependencies, RelationalDependencies));
conventionSet.EntityTypeAddedConventions.Add(tableNameFromDbSetConvention);
+ conventionSet.EntityTypeAddedConventions.Add(checkConstraintConvention);
ValueGenerationConvention valueGenerationConvention =
new RelationalValueGenerationConvention(Dependencies, RelationalDependencies);
ReplaceConvention(conventionSet.EntityTypeBaseTypeChangedConventions, valueGenerationConvention);
+ conventionSet.EntityTypeBaseTypeChangedConventions.Add(tableNameFromDbSetConvention);
+ conventionSet.EntityTypeBaseTypeChangedConventions.Add(checkConstraintConvention);
+
ReplaceConvention(conventionSet.ForeignKeyPropertiesChangedConventions, valueGenerationConvention);
+
ReplaceConvention(conventionSet.ForeignKeyOwnershipChangedConventions, valueGenerationConvention);
- conventionSet.EntityTypeBaseTypeChangedConventions.Add(tableNameFromDbSetConvention);
- conventionSet.EntityTypeBaseTypeChangedConventions.Add(new CheckConstraintConvention(Dependencies, RelationalDependencies));
conventionSet.EntityTypeAnnotationChangedConventions.Add((RelationalValueGenerationConvention)valueGenerationConvention);
diff --git a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs
index 765d6606b71..8b35e3c63bb 100644
--- a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs
+++ b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs
@@ -166,6 +166,23 @@ public static IEnumerable GetCheckConstraints(IReadOnl
return null;
}
+ ///
+ /// 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 void Attach(IConventionEntityType entityType, IConventionCheckConstraint detachedCheckConstraint)
+ {
+ var newCheckConstraint = new CheckConstraint(
+ (IMutableEntityType)entityType,
+ detachedCheckConstraint.ModelName,
+ detachedCheckConstraint.Sql,
+ detachedCheckConstraint.GetConfigurationSource());
+
+ Attach(detachedCheckConstraint, newCheckConstraint);
+ }
+
///
/// 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
@@ -180,6 +197,8 @@ public static void Attach(IConventionCheckConstraint detachedCheckConstraint, IC
((InternalCheckConstraintBuilder)existingCheckConstraint.Builder).HasName(
detachedCheckConstraint.Name, nameConfigurationSource.Value);
}
+
+ ((InternalCheckConstraintBuilder)existingCheckConstraint.Builder).MergeAnnotationsFrom((CheckConstraint)detachedCheckConstraint);
}
///
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalCheckConstraintBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalCheckConstraintBuilder.cs
index 292af98d429..9bb12353ea7 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalCheckConstraintBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalCheckConstraintBuilder.cs
@@ -16,8 +16,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public class InternalCheckConstraintBuilder :
- AnnotatableBuilder,
+ AnnotatableBuilder,
IConventionCheckConstraintBuilder
{
///
diff --git a/test/EFCore.SqlServer.Tests/ModelBuilding/SqlServerModelBuilderGenericTest.cs b/test/EFCore.SqlServer.Tests/ModelBuilding/SqlServerModelBuilderGenericTest.cs
index 947dd045a2a..2d7812e620d 100644
--- a/test/EFCore.SqlServer.Tests/ModelBuilding/SqlServerModelBuilderGenericTest.cs
+++ b/test/EFCore.SqlServer.Tests/ModelBuilding/SqlServerModelBuilderGenericTest.cs
@@ -813,37 +813,62 @@ public override void Can_configure_owned_type()
{
var modelBuilder = CreateModelBuilder();
- var ownedBuilder = modelBuilder.Entity().OwnsOne(c => c.Details)
- .ToTable("CustomerDetails")
+ modelBuilder.Ignore();
+ modelBuilder.Ignore();
+
+ var ownedBuilder = modelBuilder.Entity().OwnsOne(c => c.Details)
+ .ToTable("OtherCustomerDetails")
.HasCheckConstraint("CK_CustomerDetails_T", "AlternateKey <> 0", c => c.HasName("CK_Guid"));
ownedBuilder.Property(d => d.CustomerId);
ownedBuilder.HasIndex(d => d.CustomerId);
- ownedBuilder.WithOwner(d => d.Customer)
+ ownedBuilder.WithOwner(d => (OtherCustomer)d.Customer)
.HasPrincipalKey(c => c.AlternateKey);
+ modelBuilder.Entity().OwnsOne(c => c.Details, b =>
+ {
+ b.ToTable("SpecialCustomerDetails");
+ b.HasCheckConstraint("CK_CustomerDetails_T", "AlternateKey <> 0", c => c.HasName("CK_Guid"));
+ b.Property(d => d.CustomerId);
+ b.HasIndex(d => d.CustomerId);
+ b.WithOwner(d => (SpecialCustomer)d.Customer)
+ .HasPrincipalKey(c => c.AlternateKey);
+ });
+
var model = modelBuilder.FinalizeModel();
- var owner = model.FindEntityType(typeof(Customer));
- Assert.Equal(typeof(Customer).FullName, owner.Name);
- var ownership = owner.FindNavigation(nameof(Customer.Details)).ForeignKey;
- Assert.True(ownership.IsOwnership);
- Assert.Equal(nameof(Customer.Details), ownership.PrincipalToDependent.Name);
- Assert.Equal("CustomerAlternateKey", ownership.Properties.Single().Name);
- Assert.Equal(nameof(Customer.AlternateKey), ownership.PrincipalKey.Properties.Single().Name);
- var owned = ownership.DeclaringEntityType;
- Assert.Same(ownedBuilder.OwnedEntityType, owned);
- Assert.Equal("CustomerDetails", owned.GetTableName());
- var checkConstraint = owned.GetCheckConstraints().Single();
- Assert.Equal("CK_CustomerDetails_T", checkConstraint.ModelName);
- Assert.Equal("AlternateKey <> 0", checkConstraint.Sql);
- Assert.Equal("CK_Guid", checkConstraint.Name);
- Assert.Single(owned.GetForeignKeys());
- Assert.Equal(nameof(CustomerDetails.CustomerId), owned.GetIndexes().Single().Properties.Single().Name);
- Assert.Equal(
- new[] { "CustomerAlternateKey", nameof(CustomerDetails.CustomerId), nameof(CustomerDetails.Id) },
- owned.GetProperties().Select(p => p.Name));
- Assert.NotNull(model.FindEntityType(typeof(CustomerDetails)));
- Assert.Equal(1, model.GetEntityTypes().Count(e => e.ClrType == typeof(CustomerDetails)));
+ var owner1 = model.FindEntityType(typeof(OtherCustomer));
+ Assert.Equal(typeof(OtherCustomer).FullName, owner1.Name);
+ AssertOwnership(owner1);
+
+ var owner2 = model.FindEntityType(typeof(SpecialCustomer));
+ Assert.Equal(typeof(SpecialCustomer).FullName, owner2.Name);
+ AssertOwnership(owner2);
+
+ Assert.Null(model.FindEntityType(typeof(CustomerDetails)));
+ Assert.Equal(2, model.GetEntityTypes().Count(e => e.ClrType == typeof(CustomerDetails)));
+
+ static void AssertOwnership(IEntityType owner)
+ {
+ var ownership1 = owner.FindNavigation(nameof(Customer.Details)).ForeignKey;
+ Assert.True(ownership1.IsOwnership);
+ Assert.Equal(nameof(Customer.Details), ownership1.PrincipalToDependent.Name);
+ Assert.Equal("CustomerAlternateKey", ownership1.Properties.Single().Name);
+ Assert.Equal(nameof(Customer.AlternateKey), ownership1.PrincipalKey.Properties.Single().Name);
+ var owned = ownership1.DeclaringEntityType;
+ Assert.Equal(owner.ShortName() + "Details", owned.GetTableName());
+ var checkConstraint = owned.GetCheckConstraints().Single();
+ Assert.Same(owned, checkConstraint.EntityType);
+ Assert.Equal("CK_CustomerDetails_T", checkConstraint.ModelName);
+ Assert.Equal("AlternateKey <> 0", checkConstraint.Sql);
+ Assert.Equal("CK_Guid", checkConstraint.Name);
+ Assert.Single(owned.GetForeignKeys());
+ var index = owned.GetIndexes().Single();
+ Assert.Same(owned, index.DeclaringEntityType);
+ Assert.Equal(nameof(CustomerDetails.CustomerId), index.Properties.Single().Name);
+ Assert.Equal(
+ new[] { "CustomerAlternateKey", nameof(CustomerDetails.CustomerId), nameof(CustomerDetails.Id) },
+ owned.GetProperties().Select(p => p.Name));
+ }
}
[ConditionalFact]