From eff4df2bc5c53b077fc491eafbb01ba67a508f56 Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Fri, 15 Oct 2021 14:43:23 -0700 Subject: [PATCH] Configure the correct end as principal for self-ref collection with [ForeignKey] Fixes #26364 --- .../ForeignKeyAttributeConvention.cs | 5 ++- .../Internal/InternalEntityTypeBuilder.cs | 7 ++-- .../DataAnnotationTestBase.cs | 42 +++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/EFCore/Metadata/Conventions/ForeignKeyAttributeConvention.cs b/src/EFCore/Metadata/Conventions/ForeignKeyAttributeConvention.cs index f978752eb81..6694cc571e6 100644 --- a/src/EFCore/Metadata/Conventions/ForeignKeyAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/ForeignKeyAttributeConvention.cs @@ -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); } } diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index eaef2f1cb4f..3c7b60b1fa3 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -2825,14 +2825,15 @@ private static InternalIndexBuilder DetachIndex(Index indexToDetach) var navigationProperty = navigationToTarget?.MemberInfo; var inverseProperty = inverseNavigation?.MemberInfo; if (setTargetAsPrincipal == false - || (inverseNavigation == null + || (setTargetAsPrincipal == null + && inverseNavigation == 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 diff --git a/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs b/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs index 47a595d67fc..c7ebab2e2e6 100644 --- a/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs +++ b/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs @@ -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(); + + 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 Pages { get; set; } + + [ForeignKey(nameof(FkGroup))] + public virtual ICollection 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() {