Efcore: Consider un-deleting an entity when it has been orphaned and then reparented

Created on 11 Jan 2020  路  5Comments  路  Source: dotnet/efcore

On ef core 3.0, 3.1 and 5.0 after removing child entity from parent aggregate and adding it to another aggregate, child entity disappears from db after SaveChanges.

On ef core 2.2.6 child entity was preserved as expected.

Steps to reproduce

``` C#
class Program
{
static void Main(string[] args)
{
var db = new BloggingContext();
db.Add(new Blog());
db.Add(new Blog());
db.SaveChanges();

    db.Blogs.First().Posts.Add(new Post());
    db.SaveChanges();

    var blogs = db.Blogs.ToArray();
    var post = blogs[0].Posts.First();
    blogs[0].Posts.Remove(post);
    blogs[1].Posts.Add(post);

    var entry = db.Entry(post);
    Console.WriteLine(entry.State); // ef 2 -> Modified, ef 3 -> Deleted

    db.SaveChanges();
    blogs = db.Blogs.ToArray();

    Console.WriteLine($"{blogs[0].Posts.Count} {blogs[1].Posts.Count} {db.Posts.Count()}");
    // ef 2 -> 0 1 1, ef 3 -> 0 0 0       
}

}

public class BloggingContext : DbContext
{
public DbSet Blogs { get; set; }
public DbSet Posts { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseInMemoryDatabase("test");

}

public class Blog
{
public int BlogId { get; set; }
public List Posts { get; } = new List();
}

public class Post
{
public int PostId { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
```

customer-reported type-bug

All 5 comments

This looks like it has the same root cause as #17828. See sample repro in https://github.com/dotnet/efcore/issues/17828#issuecomment-532422761

@bachratyg It's not exactly the same as #17828 because that also involves DetectChanges. I think this case is limited to just the change to cascade timing and can be fixed by switching back to the 2.x default.

However, I want to discuss with the team, because I think we may be able to do something better here--that is, let the entity go through a deleted phase but then bring it back when it is re-added.

Thank you guys, restoring back to context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges; indeed solves the problem.

Anyway, it would be nice if that could work regardless of that setting. This is kind of most idiomatic way to change of owner in object-oriented manner.

Encountered the same issue today. Thanks for the workaround!

I'm running into an issue where we are implementing soft deletes by checking for deleted entities prior to SaveChanges and changing them to modified and setting a DateDeleted property.

When DeleteOrphansTiming is set to OnSaveChanges, it solves the re-parenting problem but does not allow orphans to be soft deleted because those orphans are marked as Modified when the soft-delete check is performed. Then when SaveChanges is called, these orphaned entities just get hard-deleted.

Was this page helpful?
0 / 5 - 0 ratings