Skip to content

Commit

Permalink
Configure the correct end as principal for self-ref collection with […
Browse files Browse the repository at this point in the history
…ForeignKey]

Fixes #26364
  • Loading branch information
AndriySvyryd committed Oct 18, 2021
1 parent bc24387 commit 65577ca
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ public virtual void ProcessEntityTypeAdded(
foreach (var navigation in foreignKeyNavigations)
{
entityTypeBuilder.HasRelationship(
entityType, foreignKeyNavigations[0], setTargetAsPrincipal: true, fromDataAnnotation: true);
entityType,
navigation,
setTargetAsPrincipal: navigation.GetMemberType().IsAssignableFrom(entityType.ClrType),
fromDataAnnotation: true);
}
}

Expand Down
20 changes: 17 additions & 3 deletions src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2823,16 +2823,30 @@ private static InternalIndexBuilder DetachIndex(Index indexToDetach)
"required should only be set if principal end is known");

var navigationProperty = navigationToTarget?.MemberInfo;
if (navigationProperty == null
&& navigationToTarget?.Name != null
&& !Metadata.IsPropertyBag)
{
navigationProperty = InternalForeignKeyBuilder.FindCompatibleClrMember(
navigationToTarget!.Value.Name!, Metadata, targetEntityType,
shouldThrow: configurationSource == ConfigurationSource.Explicit);
if (navigationProperty != null)
{
navigationToTarget = MemberIdentity.Create(navigationProperty);
}
}

var inverseProperty = inverseNavigation?.MemberInfo;
if (setTargetAsPrincipal == false
|| (inverseNavigation == null
|| (setTargetAsPrincipal == null
&& inverseNavigation?.Name == null
&& navigationProperty?.GetMemberType().IsAssignableFrom(
targetEntityType.ClrType)
== false))
{
// Target is expected to be dependent or only one nav specified and it can't be the nav to principal
// Target is dependent or only one nav specified and it can't be the nav to principal
return targetEntityType.Builder.HasRelationship(
Metadata, null, navigationToTarget, !setTargetAsPrincipal, configurationSource, required);
Metadata, inverseNavigation, navigationToTarget, setTargetAsPrincipal: true, configurationSource, required);
}

if (setTargetAsPrincipal == null
Expand Down
42 changes: 42 additions & 0 deletions test/EFCore.Specification.Tests/DataAnnotationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,48 @@ public class ProfileDetails12
public int Id { get; set; }
}

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

modelBuilder.Entity<MenuGroup>();

var model = Validate(modelBuilder);

var menuGroup = model.FindEntityType(typeof(MenuGroup));
var groupsNavigation = menuGroup.FindNavigation(nameof(MenuGroup.Groups));
Assert.Equal(nameof(MenuGroup.FkGroup), groupsNavigation.ForeignKey.Properties.Single().Name);

var pagesNavigation = menuGroup.FindNavigation(nameof(MenuGroup.Pages));
Assert.Equal(nameof(MenuPage.FkGroupNavigation), pagesNavigation.Inverse.Name);
Assert.Equal(nameof(MenuPage.FkGroup), pagesNavigation.ForeignKey.Properties.Single().Name);
}

protected class MenuGroup
{
public Guid Id { get; set; }
public Guid? FkGroup { get; set; }

[InverseProperty(nameof(MenuPage.FkGroupNavigation))]
public virtual ICollection<MenuPage> Pages { get; set; }

[ForeignKey(nameof(FkGroup))]
public virtual ICollection<MenuGroup> Groups { get; set; }
}

protected class MenuPage
{
public Guid Id { get; set; }

public Guid? FkGroup { get; set; }

[ForeignKey(nameof(FkGroup))]
[InverseProperty(nameof(MenuGroup.Pages))]
public virtual MenuGroup FkGroupNavigation { get; set; }
}


[ConditionalFact]
public virtual void Multiple_self_ref_ForeignKeys_on_navigations()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,23 @@ public void Can_add_relationship_if_principal_entity_has_no_PK()
Assert.True(fkProperty.IsShadowProperty());
}

[ConditionalFact]
public void Collection_navigation_to_principal_throws()
{
var modelBuilder = CreateModelBuilder();
var principalEntityBuilder = modelBuilder.Entity(typeof(Customer), ConfigurationSource.Explicit);
var dependentEntityBuilder = modelBuilder.Entity(typeof(Order), ConfigurationSource.Explicit);

Assert.Equal(CoreStrings.PrincipalEndIncompatibleNavigations(
nameof(Customer) + "." + nameof(Customer.Orders), nameof(Order), nameof(Order)),
Assert.Throws<InvalidOperationException>(() =>
principalEntityBuilder.HasRelationship(
dependentEntityBuilder.Metadata,
Customer.OrdersProperty,
ConfigurationSource.DataAnnotation,
targetIsPrincipal: true)).Message);
}

[ConditionalFact]
public void Can_add_relationship_if_principal_entity_PK_name_contains_principal_entity_name()
{
Expand Down

0 comments on commit 65577ca

Please sign in to comment.