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

ManyToMany join entity type with separate PK results in incorrect FK configuration #26814

Open
JakubFojtik opened this issue Nov 24, 2021 · 3 comments
Assignees
Labels
area-model-building customer-reported punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-bug
Milestone

Comments

@JakubFojtik
Copy link

When configuring a many-to-many relationship, if we define the key of the skipped entity separately, the related collection entities stop appearing in the loaded collection, despite being loaded into the change tracker and having the correct keys in database.

Even though this bug is caused by mis-configuration, it is not reported as an error, and causes a confusing situation.

Full fiddle here: https://dotnetfiddle.net/TI9G5V

For this correct & working code snippet:

modelBuilder.Entity<Blog>().HasMany(x => x.Posts).WithMany(x => x.Blogs)
	.UsingEntity<BlogPost>(
		x => x.HasOne(x => x.Post).WithMany(),
		x => x.HasOne(x => x.Blog).WithMany(),
		x => x.HasKey(x => x.OID)
	);

the bug is caused by changing it to this:

// Setting the key here causes entities to not load into collections
modelBuilder.Entity<BlogPost>().HasKey(x => x.OID);

modelBuilder.Entity<Blog>().HasMany(x => x.Posts).WithMany(x => x.Blogs)
	.UsingEntity<BlogPost>(
		x => x.HasOne(x => x.Post).WithMany(),
		x => x.HasOne(x => x.Blog).WithMany()
	);

now EF won't load Blog.Posts, despite loading the Posts into the Change tracker. It seems EF does not consider the entities related despite loading them in the filtered query.

context.Blogs.Include(x => x.Posts).First().Posts.Count
//returns 0

context.ChangeTracker.Entries<Post>().Count()
//returns 1

//database contains exactly a single Blog,Post and BlogPost that has their IDs.
context.Blogs.Single()
context.Posts.Single()
context.Posts.Single(x=>x.Blogs.Contains(context.Blogs.Single()))==context.Posts.Single()
//returns True

This happened because i set all entity primary keys first in OnModelCreating, then add properties and relations as needed. It would be nice to have an error at model build time, instead of strange behavior at query time.

Include provider and version information

EF Core version: 6.0.0
Database provider: InMemory
Target framework: .NET 6.0

@ajcvickers
Copy link
Member

@AndriySvyryd Configuring the key first results in the FK properties not being required. This also results in different cascade behavior for FKs.

Key first:

Model: 
  EntityType: Blog
    Properties: 
      BlogId (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      Name (string)
      Url (string)
    Skip navigations: 
      Posts (ICollection<Post>) CollectionPost Inverse: Blogs
    Keys: 
      BlogId PK
  EntityType: BlogPost
    Properties: 
      OID (Guid) Required PK AfterSave:Throw ValueGenerated.OnAdd
      BlogId (no field, int?) Shadow FK Index
      PostId (no field, int?) Shadow FK Index
    Navigations: 
      Blog (Blog) ToPrincipal Blog
      Post (Post) ToPrincipal Post
    Keys: 
      OID PK
    Foreign keys: 
      BlogPost {'BlogId'} -> Blog {'BlogId'} ToPrincipal: Blog ClientSetNull
      BlogPost {'PostId'} -> Post {'Id'} ToPrincipal: Post ClientSetNull
    Indexes: 
      BlogId 
      PostId 
  EntityType: Post
    Properties: 
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      Content (string)
      Title (string)
    Skip navigations: 
      Blogs (ICollection<Blog>) CollectionBlog Inverse: Posts
    Keys: 
      Id PK

Key in UsingEntity:

Model: 
  EntityType: Blog
    Properties: 
      BlogId (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      Name (string)
      Url (string)
    Skip navigations: 
      Posts (ICollection<Post>) CollectionPost Inverse: Blogs
    Keys: 
      BlogId PK
  EntityType: BlogPost
    Properties: 
      OID (Guid) Required PK AfterSave:Throw ValueGenerated.OnAdd
      BlogId (no field, int) Shadow Required FK Index
      PostId (no field, int) Shadow Required FK Index
    Navigations: 
      Blog (Blog) ToPrincipal Blog
      Post (Post) ToPrincipal Post
    Keys: 
      OID PK
    Foreign keys: 
      BlogPost {'BlogId'} -> Blog {'BlogId'} ToPrincipal: Blog Cascade
      BlogPost {'PostId'} -> Post {'Id'} ToPrincipal: Post Cascade
    Indexes: 
      BlogId 
      PostId 
  EntityType: Post
    Properties: 
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      Content (string)
      Title (string)
    Skip navigations: 
      Blogs (ICollection<Blog>) CollectionBlog Inverse: Posts
    Keys: 
      Id PK

@JakubFojtik
Copy link
Author

Today i saw the fiddle stopped working, and recreating it using EF 6 and NET 6 does not produce the bugged behavior.
I suspect dotnetfiddle was using NET 5 while showing NET 6.

I think this can be closed, unless @ajcvickers reproduced the bug on NET 6 + EF 6?

@ajcvickers
Copy link
Member

@JakubFojtik Yes, I used EF Core 6.0 for my repro.

@ajcvickers ajcvickers added the punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. label Sep 16, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0, Backlog Sep 16, 2022
@AndriySvyryd AndriySvyryd changed the title ManyToMany UsedEntity separate PK configuration bugged without error message ManyToMany join entity type with separate PK results in incorrect FK configuration Sep 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-model-building customer-reported punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-bug
Projects
None yet
Development

No branches or pull requests

3 participants