Search code examples
entity-framework-coretransactions.net-5rollback

Entity Framework Core - Error Handling on multiple contexts


I am building an API where I get a specific object sent as a JSON and then it gets converted into another object of another type, so we have sentObject and convertedObject. Now I can do this:

using (var dbContext = _dbContextFactory.CreateDbContext())
using (var dbContext2 = _dbContextFactory2.CreateDbContext())
{
    await dbContext.AddAsync(sentObject);
    await dbContext.SaveChangesAsync();
    await dbContext2.AddAsync(convertedObject);
    await dbContext2.SaveChangesAsync();
}

Now I had a problem where the first SaveChanges call went ok but the second threw an error with a datefield that was not properly set. The first SaveChanges call happened so the data is inserted in the database while the second SaveChanges failed, which cannot happen in my use-case.

What I want to do is if the second SaveChanges call goes wrong then I basically want to rollback the changes that have been made by the first SaveChanges.

My first thought was to delete cascade but the sentObject has a complex structure and I don't want to run into circular problems with delete cascade.

Is there any tips on how I could somehow rollback my changes if one of the SaveChanges calls fails?


Solution

  • You can call context.Database.BeginTransaction as follows:

                    using (var dbContextTransaction = context.Database.BeginTransaction())
                    {
                        context.Database.ExecuteSqlCommand(
                            @"UPDATE Blogs SET Rating = 5" +
                                " WHERE Name LIKE '%Entity Framework%'"
                            );
    
                        var query = context.Posts.Where(p => p.Blog.Rating >= 5);
                        foreach (var post in query)
                        {
                            post.Title += "[Cool Blog]";
                        }
    
                        context.SaveChanges();
    
                        dbContextTransaction.Commit();
                    }
    

    (taken from the docs)

    You can therefore begin a transaction for dbContext in your case and if the second command failed, call dbContextTransaction.Rollback();

    Alternatively, you can implement the cleanup logic yourself, but it would be messy to maintain that as your code here evolves in the future.