From fe18cd6166314c5663de67d18e9b92efc1ab2ef4 Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Tue, 13 Dec 2022 18:45:57 -0800 Subject: [PATCH] Avoid infinite loop during foreign key discovery Fixes #29826 --- .../ForeignKeyPropertyDiscoveryConvention.cs | 6 ++- ...reignKeyPropertyDiscoveryConventionTest.cs | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs index 46e93b26864..8335d1170fc 100644 --- a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs @@ -50,6 +50,9 @@ public class ForeignKeyPropertyDiscoveryConvention : IPropertyFieldChangedConvention, IModelFinalizingConvention { + private static readonly bool QuirkEnabled29826 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29826", out var enabled) && enabled; + /// /// Creates a new instance of . /// @@ -309,7 +312,8 @@ private IConventionForeignKeyBuilder ProcessForeignKey( : relationshipBuilder; } - if (conflictingFKCount == 0) + if ((!QuirkEnabled29826 && conflictingFKCount >= 0) + || (QuirkEnabled29826 && conflictingFKCount == 0)) { return ((ForeignKey)foreignKey).Builder.ReuniquifyImplicitProperties(false); } diff --git a/test/EFCore.Tests/Metadata/Conventions/ForeignKeyPropertyDiscoveryConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/ForeignKeyPropertyDiscoveryConventionTest.cs index ac5ba9b54ad..8195387d19c 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ForeignKeyPropertyDiscoveryConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ForeignKeyPropertyDiscoveryConventionTest.cs @@ -811,6 +811,44 @@ public void Does_not_match_if_a_foreign_key_on_the_best_candidate_property_alrea Assert.Throws(() => ValidateModel()).Message); } + [ConditionalFact] + public void Does_not_match_if_a_foreign_key_on_the_best_candidate_property_already_configured_explicitly() + { + var dependentTypeBuilder = DependentType.Builder; + var fkProperty = dependentTypeBuilder.Property(DependentEntity.SomeNavPeEKaYProperty, ConfigurationSource.Convention).Metadata; + dependentTypeBuilder.Property(DependentEntity.PrincipalEntityIDProperty, ConfigurationSource.Convention); + dependentTypeBuilder.Property(DependentEntity.PrincipalEntityPeEKaYProperty, ConfigurationSource.Convention); + dependentTypeBuilder.Property(DependentEntity.PeEKaYProperty, ConfigurationSource.Convention); + + var derivedTypeBuilder = _model.Entity(typeof(DerivedPrincipalEntity), ConfigurationSource.Convention); + derivedTypeBuilder.HasBaseType(PrincipalType, ConfigurationSource.Convention); + + var relationshipBuilder = dependentTypeBuilder + .HasRelationship(derivedTypeBuilder.Metadata, new[] { fkProperty }, ConfigurationSource.Explicit); + var compositeRelationshipBuilder = dependentTypeBuilder + .HasRelationship(PrincipalTypeWithCompositeKey, new[] { fkProperty }, ConfigurationSource.Explicit); + + var newRelationshipBuilder = dependentTypeBuilder.HasRelationship( + PrincipalType, "SomeNav", null, ConfigurationSource.Convention); + + Assert.Equal( + "SomeNav" + nameof(PrincipalEntity.PeeKay), + newRelationshipBuilder.Metadata.Properties.Single().Name); + + newRelationshipBuilder = RunConvention(newRelationshipBuilder); + + var fk = (IReadOnlyForeignKey)relationshipBuilder.Metadata; + Assert.Same(fkProperty, fk.Properties.Single()); + Assert.False(fk.IsUnique); + + var newFk = newRelationshipBuilder.Metadata; + Assert.Equal(3, DependentType.GetForeignKeys().Count()); + Assert.Equal("SomeNav" + nameof(PrincipalEntity.PeeKay), newFk.Properties.Single().Name); + Assert.Null(newFk.GetPropertiesConfigurationSource()); + + ValidateModel(); + } + [ConditionalFact] public void Logs_warning_if_foreign_key_property_names_are_order_dependent() {