Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack overflow when using circular composite key dependencies #26629

Closed
Stratster opened this issue Nov 11, 2021 · 1 comment
Closed

Stack overflow when using circular composite key dependencies #26629

Stratster opened this issue Nov 11, 2021 · 1 comment
Labels
area-model-building closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported regression Servicing-approved type-bug
Milestone

Comments

@Stratster
Copy link

After upgrading to .net6 release version 6.0.0 from 6.0.0-rc2 we encountered a fatal stack overflow while running our application.
We searched for a while and found that our cyclic entity dependencies with composite keys are now broken.

I attached a very simple test project for reproducing the error.
It is a very simple file structure: 2 entities and the DbContext

public partial class Parent
{
    public long ID { get; set; }

    public long? FavoriteChildID { get; set; }

    public virtual Child FavoriteChild { get; set; }

    public virtual List<Child> Children { get; set; }
}

public partial class Child
{
    public long ParentID { get; set; } // Key order 1

    public long ID { get; set; } // Key order 2

    public virtual Parent Parent { get; set; }
}

public partial class Context : DbContext
{
    public Context()
    {
    }

    public Context(DbContextOptions<Context> options)
        : base(options)
    {
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
            optionsBuilder.UseInMemoryDatabase("test");
    }

    public virtual DbSet<Parent> Parents { get; set; }
    public virtual DbSet<Child> Children { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Parent>(entity =>
        {
            entity.HasKey(d => d.ID);

            entity.HasOne(d => d.FavoriteChild)
                .WithOne()
                .HasForeignKey<Parent>(d => new { d.ID, d.FavoriteChildID });
        });

        modelBuilder.Entity<Child>(entity =>
        {
            entity.HasKey(d => new { d.ParentID, d.ID });

            entity.HasOne(d => d.Parent)
                .WithMany(p => p.Children)
                .HasForeignKey(d => d.ParentID);
        });

        OnModelCreatingPartial(modelBuilder);
    }

    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}

The main method of the project is as simple as

using BreakEFCore6;

var ctx = new Context();

var q = from p in ctx.Parents
        where p.FavoriteChildID.HasValue
        select p;

var parents = q.ToList();

This error occured with the provided InMemory-database as well as our SqlServer in production. (Seems provider independent)

While researching the error we assume this fix could have something to do with it:
Commit: 539aee7e60694dd608d82a4884908b8f1127a39e
We added some printf-debugging in

Microsoft.EntityFrameworkCore.Metadata.Internal.Property.cs

public virtual ValueComparer? GetValueComparer()
{
    Console.WriteLine(this.ToString()); // THIS LINE HERE
    return (ValueComparer?)this[CoreAnnotationNames.ValueComparer]
           ?? FindFirstDifferentPrincipal()?.GetValueComparer()
           ?? TypeMapping?.Comparer;
}

which results in:

Property: Child.ParentID (long) Required PK FK AfterSave:Throw
Property: Parent.ID (long) Required PK FK Index AfterSave:Throw
Property: Child.ParentID (long) Required PK FK AfterSave:Throw
Property: Parent.ID (long) Required PK FK Index AfterSave:Throw
Property: Child.ParentID (long) Required PK FK AfterSave:Throw
Property: Parent.ID (long) Required PK FK Index AfterSave:Throw
Property: Child.ParentID (long) Required PK FK AfterSave:Throw
Property: Parent.ID (long) Required PK FK Index AfterSave:Throw
Property: Child.ParentID (long) Required PK FK AfterSave:Throw
Property: Parent.ID (long) Required PK FK Index AfterSave:Throw
...

And the stack overflow output is:

Stack overflow.
   at System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at Microsoft.EntityFrameworkCore.Metadata.IReadOnlyProperty.FindFirstPrincipal()
   at Microsoft.EntityFrameworkCore.Metadata.IProperty.FindFirstPrincipal()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.FindFirstDifferentPrincipal()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.Microsoft.EntityFrameworkCore.Metadata.IProperty.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.Microsoft.EntityFrameworkCore.Metadata.IProperty.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.Microsoft.EntityFrameworkCore.Metadata.IProperty.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.Microsoft.EntityFrameworkCore.Metadata.IProperty.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.Microsoft.EntityFrameworkCore.Metadata.IProperty.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.Microsoft.EntityFrameworkCore.Metadata.IProperty.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.Microsoft.EntityFrameworkCore.Metadata.IProperty.GetValueComparer()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Property.GetValueComparer()
...

Minimal test project

BreakEFCore6.zip

@ajcvickers ajcvickers self-assigned this Nov 11, 2021
ajcvickers added a commit that referenced this issue Nov 15, 2021
…onships

Fixes #26629

**Description**

We added code in 6.0 to find the corresponding principal key property of a foreign key property when looking for value comparers. However, we missed the case where the foreign key property is itself the principal key for another overlapping relationship. This can happen when two entity types act as principal and dependents of each other, with one having a composite foreign key containing the principal key of the other. In this case, the code goes into an infinite loop.

**Customer impact**

Stack overflow when a model of this form is used. This is not a super common model, but is also not extremely rare, so we expect many more people to hit this as the update to 6.0.

**How found**

Customer reported on 6.0.

**Regression**

Yes, from 5.0.

**Testing**

Added this model to our tests.

**Risk**

Low; keep track of the properties we have checked so we can avoid cycles. Also added a quirk mode.
@ajcvickers ajcvickers added this to the 6.0.1 milestone Nov 15, 2021
@ajcvickers ajcvickers added closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. Servicing-approved and removed Servicing-consider labels Nov 15, 2021
@ajcvickers
Copy link
Member

FYI for those impacted by this issue: EF Core 6.0.1 is now available from NuGet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-model-building closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported regression Servicing-approved type-bug
Projects
None yet
Development

No branches or pull requests

3 participants