diff --git a/src/EFCore/Metadata/Internal/InternalModelBuilder.cs b/src/EFCore/Metadata/Internal/InternalModelBuilder.cs
index bf4d2723bf6..58e212ab021 100644
--- a/src/EFCore/Metadata/Internal/InternalModelBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalModelBuilder.cs
@@ -82,12 +82,25 @@ public InternalModelBuilder([NotNull] Model metadata)
}
if (shouldBeOwned == true
- && entityType != null
- && !entityType.IsOwned()
- && configurationSource == ConfigurationSource.Explicit
- && entityType.GetConfigurationSource() == ConfigurationSource.Explicit)
+ && entityType != null)
{
- throw new InvalidOperationException(CoreStrings.ClashingNonOwnedEntityType(entityType.DisplayName()));
+ if (!entityType.IsOwned()
+ && configurationSource == ConfigurationSource.Explicit
+ && entityType.GetConfigurationSource() == ConfigurationSource.Explicit)
+ {
+ throw new InvalidOperationException(CoreStrings.ClashingNonOwnedEntityType(entityType.DisplayName()));
+ }
+
+ foreach (var derivedType in entityType.GetDerivedTypes())
+ {
+ if (!derivedType.IsOwned()
+ && configurationSource == ConfigurationSource.Explicit
+ && derivedType.GetConfigurationSource() == ConfigurationSource.Explicit)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.ClashingNonOwnedDerivedEntityType(entityType.DisplayName(), derivedType.DisplayName()));
+ }
+ }
}
if (entityType != null)
diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index 0524bd06596..126b1469076 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -2198,6 +2198,14 @@ public static string IncludeOnEntityWithDefiningQueryNotSupported([CanBeNull] ob
GetString("IncludeOnEntityWithDefiningQueryNotSupported", nameof(entityType)),
entityType);
+ ///
+ /// The type '{entityType}' cannot be marked as owned because the derived entity type - '{derivedType}' has been configured as non-owned.
+ ///
+ public static string ClashingNonOwnedDerivedEntityType([CanBeNull] object entityType, [CanBeNull] object derivedType)
+ => string.Format(
+ GetString("ClashingNonOwnedDerivedEntityType", nameof(entityType), nameof(derivedType)),
+ entityType, derivedType);
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index 26b5a6527d6..b10482078ae 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -1219,4 +1219,7 @@
'{principalEntityType}.{principalNavigation}' may still be null at runtime despite being declared as non-nullable since only the navigation to principal can be configured as required.
Debug CoreEventId.NonNullableReferenceOnDependent string string
-
+
+ The type '{entityType}' cannot be marked as owned because the derived entity type - '{derivedType}' has been configured as non-owned.
+
+
\ No newline at end of file
diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs
index 1c9280cb390..a4650fac077 100644
--- a/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs
+++ b/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs
@@ -157,12 +157,7 @@ public TestModelBuilder HasAnnotation(string annotation, object value)
public abstract TestModelBuilder Ignore()
where TEntity : class;
- public virtual TestModelBuilder FinalizeModel()
- {
- ModelBuilder.FinalizeModel();
-
- return this;
- }
+ public virtual IModel FinalizeModel() => ModelBuilder.FinalizeModel();
public virtual string GetDisplayName(Type entityType) => entityType.Name;
diff --git a/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs b/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs
index fd17645677f..765ad3181d0 100644
--- a/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs
+++ b/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs
@@ -1129,6 +1129,75 @@ public virtual void Reconfiguring_owned_type_as_non_owned_throws()
modelBuilder.Entity().HasOne(c => c.Details)).Message);
}
+ [ConditionalFact]
+ public virtual void Deriving_from_owned_type_throws()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity()
+ .Ignore(b => b.AlternateLabel)
+ .Ignore(b => b.Details)
+ .OwnsOne(b => b.Label, lb =>
+ {
+ lb.Ignore(l => l.AnotherBookLabel);
+ lb.Ignore(l => l.SpecialBookLabel);
+ });
+
+ Assert.Equal(
+ CoreStrings.ClashingOwnedEntityType(nameof(AnotherBookLabel)),
+ Assert.Throws(
+ () => modelBuilder.Entity()).Message);
+ }
+
+ [ConditionalFact]
+ public virtual void Configuring_base_type_as_owned_throws()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity();
+
+ modelBuilder.Entity()
+ .Ignore(b => b.AlternateLabel)
+ .Ignore(b => b.Details);
+
+ Assert.Equal(
+ CoreStrings.ClashingNonOwnedDerivedEntityType(nameof(BookLabel), nameof(AnotherBookLabel)),
+ Assert.Throws(
+ () =>
+ modelBuilder.Entity().OwnsOne(c => c.Label)).Message);
+ }
+
+ [ConditionalFact]
+ public virtual void CLR_base_type_can_be_owned_when_not_in_hierarchy()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity()
+ .HasBaseType(null)
+ .Ignore(l => l.Book)
+ .Ignore(l => l.SpecialBookLabel)
+ .Ignore(l => l.AnotherBookLabel);
+
+ modelBuilder.Entity()
+ .Ignore(b => b.AlternateLabel)
+ .Ignore(b => b.Details)
+ .OwnsOne(c => c.Label, lb =>
+ {
+ lb.Ignore(l => l.AnotherBookLabel);
+ lb.Ignore(l => l.SpecialBookLabel);
+ });
+
+ var model = modelBuilder.FinalizeModel();
+
+ var bookLabelOwnership = model.FindEntityType(typeof(Book)).FindNavigation(nameof(Book.Label))
+ .ForeignKey;
+
+ Assert.True(bookLabelOwnership.IsOwnership);
+ Assert.Equal(nameof(BookLabel.Book), bookLabelOwnership.DependentToPrincipal.Name);
+
+ Assert.Null(model.FindEntityType(typeof(AnotherBookLabel)).BaseType);
+ }
+
[ConditionalFact]
public virtual void OwnedType_can_derive_from_Collection()
{