Skip to content

StackOverflowException with EFCore 5 many-to-many mapping using many-to-one via self #23377

New issue

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

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

Already on GitHub? # to your account

Closed
FransBouma opened this issue Nov 18, 2020 · 7 comments

Comments

@FransBouma
Copy link

FransBouma commented Nov 18, 2020

EF Core version: 5.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 5.0
Operating system: Windows 10
IDE: (e.g. Visual Studio 2019 16.8)

Stacktrace:

long stackoverflow stacktrace

Stack overflow.
at System.Globalization.CompareInfo.IcuCompareString(System.ReadOnlySpan1<Char>, System.ReadOnlySpan1, System.Globalization.CompareOptions)
at System.Globalization.CompareInfo.Compare(System.ReadOnlySpan1<Char>, System.ReadOnlySpan1, System.Globalization.CompareOptions)
at System.Globalization.CompareInfo.Compare(System.String, System.String, System.Globalization.CompareOptions)
at System.String.Compare(System.String, System.String, System.StringComparison)
at System.String.CompareTo(System.String)
at System.Collections.Generic.GenericComparer1[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Compare(System.__Canon, System.__Canon) at System.Collections.Generic.SortedDictionary2+KeyValuePairComparer[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Compare(System.Collections.Generic.KeyValuePair2<System.__Canon,System.__Canon>, System.Collections.Generic.KeyValuePair2<System.__Canon,System.__Canon>)
at System.Collections.Generic.SortedSet1[[System.Collections.Generic.KeyValuePair2[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].FindNode(System.Collections.Generic.KeyValuePair2<System.__Canon,System.__Canon>) at System.Collections.Generic.SortedDictionary2[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryGetValue(System.__Canon, System.__Canon ByRef)
at Microsoft.EntityFrameworkCore.Infrastructure.Annotatable.FindAnnotation(System.String)
at Microsoft.EntityFrameworkCore.Infrastructure.Annotatable.get_Item(System.String)
at Microsoft.EntityFrameworkCore.RelationalModelExtensions.GetDefaultSchema(Microsoft.EntityFrameworkCore.Metadata.IModel)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetDefaultSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions+<>c__DisplayClass6_0.b__4(Microsoft.EntityFrameworkCore.Metadata.ISkipNavigation)
at System.Linq.Enumerable.All[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Generic.IEnumerable1<System.__Canon>, System.Func2<System.__Canon,Boolean>)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetDefaultSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions+<>c__DisplayClass6_0.b__4(Microsoft.EntityFrameworkCore.Metadata.ISkipNavigation)
at System.Linq.Enumerable.All[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Generic.IEnumerable1<System.__Canon>, System.Func2<System.__Canon,Boolean>)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetDefaultSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions+<>c__DisplayClass6_0.b__4(Microsoft.EntityFrameworkCore.Metadata.ISkipNavigation)
at System.Linq.Enumerable.All[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Generic.IEnumerable1<System.__Canon>, System.Func2<System.__Canon,Boolean>)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetDefaultSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions+<>c__DisplayClass6_0.b__4(Microsoft.EntityFrameworkCore.Metadata.ISkipNavigation)
at System.Linq.Enumerable.All[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Generic.IEnumerable1<System.__Canon>, System.Func2<System.__Canon,Boolean>)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetDefaultSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions.GetSchema(Microsoft.EntityFrameworkCore.Metadata.IEntityType)
at Microsoft.EntityFrameworkCore.RelationalEntityTypeExtensions+<>c__DisplayClass6_0.b__4(Microsoft.EntityFrameworkCore.Metadata.ISkipNavigation)
at System.Linq.Enumerable.All[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Generic.IEnumerable1<System.__Canon>, System.Func2<System.__Canon,Boolean>)
at
...
etc.

Attached you'll find a repro case. The culprit is this relationship:

protected virtual void MapEmployee(EntityTypeBuilder<Employee> config)
{
	config.ToTable("Employees");
	config.HasKey(t => t.EmployeeId);
	config.Property(t => t.EmployeeId).HasColumnName("EmployeeID").ValueGeneratedOnAdd();
	config.Property(t => t.LastName).HasMaxLength(20).IsRequired();
//...
	config.Property(t => t.RegionId).HasColumnName("RegionID");
	config.OwnsOne(t => t.Address).Property(t => t.Address).HasColumnName("Address").HasMaxLength(60);
	config.OwnsOne(t => t.Address).Property(t => t.City).HasColumnName("City").HasMaxLength(15);
	config.OwnsOne(t => t.Address).Property(t => t.Country).HasColumnName("Country").HasMaxLength(15).IsRequired();
	config.OwnsOne(t => t.Address).OwnsOne(t => t.ZipCodeVtField).Property(t => t.PostalCode).HasColumnName("PostalCode").HasMaxLength(10);
	config.Navigation(t=>t.Address).IsRequired();
	config.HasOne(t => t.Manager).WithMany(t => t.Employees).HasForeignKey(t => t.ReportsTo);
	config.HasOne(t => t.Region1).WithMany(t => t.Employees).HasForeignKey(t => t.RegionId);
	config.HasMany(t => t.RegionCollectionViaEmployee).WithMany(t=>t.EmployeeCollectionViaEmployee).UsingEntity<Employee>(t=>t.HasOne(a=>a.Region1).WithMany(b=>b.Employees), t=>t.HasOne(a=>a.Manager).WithMany(b=>b.Employees));
	config.HasMany(t => t.ShipperCollectionViaOrder).WithMany(t=>t.EmployeeCollectionViaOrder).UsingEntity<Order>(t=>t.HasOne(a=>a.Shipper).WithMany(b=>b.Orders), t=>t.HasOne(a=>a.Employee).WithMany(b=>b.Orders));
	config.HasMany(t => t.TerritoryCollectionViaEmployeeTerritory).WithMany(t=>t.EmployeeCollectionViaEmployeeTerritory).UsingEntity<EmployeeTerritory>(t=>t.HasOne(a=>a.Territory).WithMany(b=>b.EmployeeTerritories), t=>t.HasOne(a=>a.Employee).WithMany(b=>b.EmployeeTerritories));
}

this one:

	config.HasMany(t => t.RegionCollectionViaEmployee).WithMany(t=>t.EmployeeCollectionViaEmployee).UsingEntity<Employee>(t=>t.HasOne(a=>a.Region1).WithMany(b=>b.Employees), t=>t.HasOne(a=>a.Manager).WithMany(b=>b.Employees));

It has a m:n relationship via a relationship with itself. Commenting this m:n relationship mapping fixes it.
It might be I made a mapping mistake, but I don't see how I can map it otherwise.

Repro:
EF5CoreStackOverflowWithMNViaSelf.zip

@ajcvickers
Copy link
Contributor

@AndriySvyryd Thoughts?

@smitpatel
Copy link
Contributor

Just want to point out that

config.HasMany(t => t.RegionCollectionViaEmployee).WithMany(t=>t.EmployeeCollectionViaEmployee).UsingEntity<Employee>(t=>t.HasOne(a=>a.Region1).WithMany(b=>b.Employees), t=>t.HasOne(a=>a.Manager).WithMany(b=>b.Employees));

This line is configuring M2M between Employee and Region using Employee as join entity. The 2nd FK in UsingEntity is also self-ref.

@FransBouma
Copy link
Author

It's indeed a self-referencing m:n with itself as the intermediate, as there are two m:1 relationships with the same entity as the 'm' side. It's a bit of an edge case perhaps although self-referencing relationships are sometimes used for m:n relationships...

@AndriySvyryd
Copy link
Member

This isn't something that we support in 5.0, but we should fix the SOE

@FransBouma
Copy link
Author

Ok, I'll make a validation rule to weed out this situation for now :)

@FransBouma
Copy link
Author

@AndriySvyryd

This isn't something that we support in 5.0, but we should fix the SOE

'This' means: a m:n based on a relationship with self or a m:n with self or both?

@ajcvickers
Copy link
Contributor

Note from triage: we will bring this to Tactics to patch for a better exception than the stack overflow.

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

4 participants