You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have a ManyToMany relationship that I want to update. My steps to update are:
Load entity from Db, including the related collection
Clear the collection
Add all the related entities back in from a disconnected entity (the updated state from the frontend)
This works with a OneToMany relationship. Which surprised me, because I expected the familiar "entity with the same key value is already being tracked" exception.
I know how to solve my particular problem, but I'm curios if there is a way to make the "just clear and add the new state" method work on ManyToMany relationships?
Am I even supposed to use this method on OneToMany relationships or does it have any bad effects?
I saw that it fires a full UPDATE statement for every child and every field. Not optimal, but if I know about this behavior I can decide if the simpler code is worth it for me.
I would have expected this drawback to not even exist for the ManyToMany case, since its only updating/creating the joinEntity.
I created a fully reproducible sample.
// One To Many works
using (var seedContext = new Context())
{
seedContext.Database.Migrate();
var parentWithChild = new Parent();
parentWithChild.Children.Add(new Child());
seedContext.Add(parentWithChild);
await seedContext.SaveChangesAsync();
}
using (var updateContext = new Context())
{
var disconnectedParent = updateContext.Parents
.AsNoTracking()
.Include(x => x.Children)
.First();
var trackedParent = updateContext.Parents
.Include(x => x.Children)
.First();
trackedParent.Children.Clear();
trackedParent.Children.AddRange(disconnectedParent.Children);
var entries = updateContext.ChangeTracker.Entries().ToList();
// = 3 Entries (1 Parent Unchanged, 1 Child (shared) Deleted, 1 Child (shared) Modified
}
// Many To Many does not work
using (var seedContext = new Context())
{
var post = new Post();
post.Tags.Add(new Tag());
seedContext.Add(post);
await seedContext.SaveChangesAsync();
}
using (var updateContext = new Context())
{
var disconnectedPost = updateContext.Posts
.AsNoTracking()
.Include(x => x.Tags)
.First();
var trackedPost = updateContext.Posts
.Include(x => x.Tags)
.First();
trackedPost.Tags.Clear();
trackedPost.Tags.AddRange(disconnectedPost.Tags);
var entries = updateContext.ChangeTracker.Entries().ToList();
// crashes with "Entity with the same key is already being tracked" Exception
}
public class Context : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connStr = new SqliteConnectionStringBuilder { DataSource = $@"file:db?mode=memory&cache=shared" }.ConnectionString;
optionsBuilder.UseSqlite(connStr);
base.OnConfiguring(optionsBuilder);
}
}
public class Parent
{
public Guid Id { get; set; }
public List<Child> Children { get; set; } = new();
}
public class Child
{
public Guid Id { get; set; }
public Parent Parent { get; set; }
public Guid ParentId { get; set; }
}
public class Post
{
public Guid Id { get; set; }
public List<Tag> Tags { get; set; } = new();
}
public class Tag
{
public Guid Id { get; set; }
public List<Post> Posts { get; set; } = new();
}
Include provider and version information
EF Core version: 6.0.3
Database provider: Sqlite
The text was updated successfully, but these errors were encountered:
The first case works because DetectChanges sees the new entity has a generated key and hence marks the new entity has Modified instead of Added. This doesn't work in the second case because clearing the collection only marks the join entities as Deleted, leaving the actual Tag entities as Unchanged:
Post {Id: 09529e59-ed3d-4eba-9e1c-ecb334f25df8} Unchanged
Id: '09529e59-ed3d-4eba-9e1c-ecb334f25df8' PK
Tags: []
PostTag (Dictionary<string, object>) {PostsId: 09529e59-ed3d-4eba-9e1c-ecb334f25df8, TagsId: a37debfb-abaf-492d-972e-af7ca3ea9dea} Deleted
PostsId: '09529e59-ed3d-4eba-9e1c-ecb334f25df8' PK FK
TagsId: 'a37debfb-abaf-492d-972e-af7ca3ea9dea' PK FK
Tag {Id: a37debfb-abaf-492d-972e-af7ca3ea9dea} Unchanged
Id: 'a37debfb-abaf-492d-972e-af7ca3ea9dea' PK
Posts: []
This is technically correct, since clearing the collection does not mean that the entities cannot exist, like it does for dependents of a required one-to-many. However, we should look into making the deletion of all entities in a skip navigation. See also #27436, which is different, but also about managing many-to-many relationships.
I have a ManyToMany relationship that I want to update. My steps to update are:
This works with a OneToMany relationship. Which surprised me, because I expected the familiar "entity with the same key value is already being tracked" exception.
I know how to solve my particular problem, but I'm curios if there is a way to make the "just clear and add the new state" method work on ManyToMany relationships?
Am I even supposed to use this method on OneToMany relationships or does it have any bad effects?
I saw that it fires a full UPDATE statement for every child and every field. Not optimal, but if I know about this behavior I can decide if the simpler code is worth it for me.
I would have expected this drawback to not even exist for the ManyToMany case, since its only updating/creating the joinEntity.
I created a fully reproducible sample.
Include provider and version information
EF Core version: 6.0.3
Database provider: Sqlite
The text was updated successfully, but these errors were encountered: