Skip to content

Commit

Permalink
Throw when deriving from an owned type.
Browse files Browse the repository at this point in the history
Fixes #17429
  • Loading branch information
AndriySvyryd committed Aug 29, 2019
1 parent 554ec09 commit c38ec37
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 12 deletions.
23 changes: 18 additions & 5 deletions src/EFCore/Metadata/Internal/InternalModelBuilder.cs
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions src/EFCore/Properties/CoreStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion src/EFCore/Properties/CoreStrings.resx
Expand Up @@ -1219,4 +1219,7 @@
<value>'{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.</value>
<comment>Debug CoreEventId.NonNullableReferenceOnDependent string string</comment>
</data>
</root>
<data name="ClashingNonOwnedDerivedEntityType" xml:space="preserve">
<value>The type '{entityType}' cannot be marked as owned because the derived entity type - '{derivedType}' has been configured as non-owned.</value>
</data>
</root>
7 changes: 1 addition & 6 deletions test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs
Expand Up @@ -157,12 +157,7 @@ public TestModelBuilder HasAnnotation(string annotation, object value)
public abstract TestModelBuilder Ignore<TEntity>()
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;

Expand Down
69 changes: 69 additions & 0 deletions test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs
Expand Up @@ -1129,6 +1129,75 @@ public virtual void Reconfiguring_owned_type_as_non_owned_throws()
modelBuilder.Entity<SpecialCustomer>().HasOne(c => c.Details)).Message);
}

[ConditionalFact]
public virtual void Deriving_from_owned_type_throws()
{
var modelBuilder = CreateModelBuilder();

modelBuilder.Entity<Book>()
.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<InvalidOperationException>(
() => modelBuilder.Entity<AnotherBookLabel>()).Message);
}

[ConditionalFact]
public virtual void Configuring_base_type_as_owned_throws()
{
var modelBuilder = CreateModelBuilder();

modelBuilder.Entity<AnotherBookLabel>();

modelBuilder.Entity<Book>()
.Ignore(b => b.AlternateLabel)
.Ignore(b => b.Details);

Assert.Equal(
CoreStrings.ClashingNonOwnedDerivedEntityType(nameof(BookLabel), nameof(AnotherBookLabel)),
Assert.Throws<InvalidOperationException>(
() =>
modelBuilder.Entity<Book>().OwnsOne(c => c.Label)).Message);
}

[ConditionalFact]
public virtual void CLR_base_type_can_be_owned_when_not_in_hierarchy()
{
var modelBuilder = CreateModelBuilder();

modelBuilder.Entity<AnotherBookLabel>()
.HasBaseType(null)
.Ignore(l => l.Book)
.Ignore(l => l.SpecialBookLabel)
.Ignore(l => l.AnotherBookLabel);

modelBuilder.Entity<Book>()
.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()
{
Expand Down

0 comments on commit c38ec37

Please sign in to comment.