Skip to content

Commit e442d41

Browse files
authored
[release/8.0] Avoid infinite recursion on identifying shadow FKs (#34891)
Fixes #34875
1 parent 893bf3e commit e442d41

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public class ForeignKeyPropertyDiscoveryConvention :
5050
IPropertyFieldChangedConvention,
5151
IModelFinalizingConvention
5252
{
53+
private static readonly bool UseOldBehavior34875 =
54+
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue34875", out var enabled34875) && enabled34875;
55+
5356
/// <summary>
5457
/// Creates a new instance of <see cref="ForeignKeyPropertyDiscoveryConvention" />.
5558
/// </summary>
@@ -142,7 +145,7 @@ private IConventionForeignKeyBuilder ProcessForeignKey(
142145
&& fkProperty.ClrType.IsNullableType() == foreignKey.IsRequired
143146
&& fkProperty.GetContainingForeignKeys().All(otherFk => otherFk.IsRequired == foreignKey.IsRequired))
144147
{
145-
var newType = fkProperty.ClrType.MakeNullable(!foreignKey.IsRequired);
148+
var newType = fkProperty.ClrType.MakeNullable(!foreignKey.IsRequired && (!fkProperty.IsKey() || UseOldBehavior34875));
146149
if (fkProperty.ClrType != newType)
147150
{
148151
fkProperty.DeclaringType.Builder.Property(

test/EFCore.Specification.Tests/ModelBuilding101ManyToManyTestBase.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,61 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
737737
}
738738
}
739739

740+
[ConditionalFact]
741+
public virtual void ManyToManyWithPayloadAndNavsToJoinClassShadowFKsTest()
742+
=> Model101Test();
743+
744+
protected class ManyToManyWithPayloadAndNavsToJoinClassShadowFKs
745+
{
746+
public class Post
747+
{
748+
public int Id { get; set; }
749+
public List<Tag> Tag { get; } = [];
750+
public List<PostTag> PostTags { get; } = [];
751+
}
752+
753+
public class Tag
754+
{
755+
public int Id { get; set; }
756+
public List<Post> Post { get; } = [];
757+
public List<PostTag> PostTags { get; } = [];
758+
}
759+
760+
public class PostTag
761+
{
762+
}
763+
764+
public class Context0 : Context101
765+
{
766+
public DbSet<Post> Post
767+
=> Set<Post>();
768+
769+
public DbSet<Tag> Tag
770+
=> Set<Tag>();
771+
772+
protected override void OnModelCreating(ModelBuilder modelBuilder)
773+
=> modelBuilder.Entity<Post>()
774+
.HasMany(e => e.Tag)
775+
.WithMany(e => e.Post)
776+
.UsingEntity<PostTag>(
777+
l => l.HasOne<Tag>().WithMany(t => t.PostTags),
778+
r => r.HasOne<Post>().WithMany(p => p.PostTags),
779+
j => { });
780+
}
781+
782+
public class Context1 : Context0
783+
{
784+
protected override void OnModelCreating(ModelBuilder modelBuilder)
785+
=> modelBuilder.Entity<Post>()
786+
.HasMany(e => e.Tag)
787+
.WithMany(e => e.Post)
788+
.UsingEntity<PostTag>(
789+
l => l.HasOne<Tag>().WithMany(t => t.PostTags).HasForeignKey("TagId"),
790+
r => r.HasOne<Post>().WithMany(p => p.PostTags).HasForeignKey("PostId"),
791+
j => { });
792+
}
793+
}
794+
740795
[ConditionalFact]
741796
public virtual void ManyToManyWithNoCascadeDeleteTest()
742797
=> Model101Test();

0 commit comments

Comments
 (0)