entity-framework-corerollbacksavechangeschange-trackingcancel-button

Using EF Core with WPF and I'd like to revert to initial state when a user press Cancel instead of OK on a dialog, is that possible and how?


I'm using EF Core with WPF and I'd like to revert any changes done on any objects to their initial state when a user press Cancel instead of OK on a dialog, is that possible and how?

I'm using a global singleton DbContext where I load all my data model at the beginning of the application. I do not want to know if I should or should not use a DataContext singleton.

When a user has to do some changes on instances in a database, I present a WPF DialogBox where he/she can choose OK or Cancel. On OK, I just do ctx.SaveChanges(). But for Cancel, how can I revert back every changes? How can I come back to a state where all objects returns to their initial state as when the Dialog was called?

I can Dispose the DataContext (which will flush all changes) and reload everything again, but it takes a lot of time. Is there a better way to achieve the task more efficiently by using changes tracked by the DbContext?

I found a GitHub-dotnet/efcore request: Implement RejectChanges() in DbContext #14594. but it doesn’t seem to have any solution.

I think the proper solution should be close to this answer for EF (not core): DbContext discard changes without disposing. I will try to code it (if possible) but an already properly coded solution, and debugged, would be so great!

Update 2022-05-27

After a few trials and errors (like having a singleton Context), I decided to go with something that would require more work, but that with be more in line with the EF Core philosophy. In my case, I load the full model (almost) in memory with "NoTracking". Then when I want to edit an instance (entity), I do so by copying it and makes modification on the copy. If the user choose to apply modifications, then I open a Context and attach to the entity to edit, apply changes to the original entity (copy changes from the copy), and then Ctx.SaveChanges and then Dispose().


Solution

  • Q: Is that possible and how?

    A: Sure.

    1. The most straightforward way is to only "SaveChanges()" when you're "Done" (when the user clicks OK).

    2. Another approach might be to make copies of the original objects, and perform in-process operations on the copies.

    3. If you're continually updating and saving work-in-progress to the DB (possibly an unwise design), then you can also leverage transactions, and rollback if the user hits Cancel.

    Example:

    https://learn.microsoft.com/en-us/ef/core/saving/transactions

    using var context = new BloggingContext();
    using var transaction = context.Database.BeginTransaction();
    
    try
    {
        context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
        context.SaveChanges();
    
        context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
        context.SaveChanges();
    
        var blogs = context.Blogs
            .OrderBy(b => b.Url)
            .ToList();
    
        // Commit transaction if all commands succeed, transaction will auto-rollback
        // when disposed if either commands fails
        transaction.Commit();
    }
    catch (Exception)
    {
        // TODO: Handle failure
    }