Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transactional SaveChangesAsync in EF

I want to save changes to a list of entities Personin the database transactionally. I have implemented the function but I don't know if I have to wrap the await Task.WhenAll(tasks); in a TransactionScope or it is already my code enough to get it.

    public class MyService {

        public MyContext Context { get; }

        public MyService(
            IDatabaseInitializer<MyContext> initializer
        ) {
            Context = new MyContext(initializer);
        }

        public async Task<int> AddOrUpdateDataAsync(IEnumerable<Person> persons)
        {
            List<Task> tasks = new List<Task>();
            try
            {
                foreach (Person person in persons)
                {
                    person.Status = Status.Finish;
                    person.Changed = DateTime.Now;
                    person.Role = Role.Worker;
                    MyContext.Persons.AddOrUpdate(person);
                    tasks.Add(await MyContext.SaveChangesAsync(););
                }

                await Task.WhenAll(tasks);
                return 1;
            }
            catch (EntityCommandExecutionException ex)
            {
                return 0;
            }
        }

    }
like image 250
MrScf Avatar asked Sep 02 '25 16:09

MrScf


2 Answers

Unless you are exactly sure about what you are doing, I do not suggest you to handle an exception by returning 0. I always prefer to handle exception at the outer most stack and log exception then take proper action. And If it is possible I suggest you to use Error handling middleware ( this varies according to the framework you use, approach you use as well as the situation).

On the other hand, you do not need to update every object one by one. You can call SaveChangesAsync after all the operation done.

try
{
    foreach (Person person in persons)
    {
        person.Status = Status.Finish;
        person.Changed = DateTime.Now;
        person.Role = Role.Worker;
        MyContext.Persons.AddOrUpdate(person);
    }

    var affectedRows = await MyContext.SaveChangesAsync();
    return (int)(persons.Count == affectedRows);
}
catch (EntityCommandExecutionException ex)
{
    return 0;
}
like image 177
Derviş Kayımbaşıoğlu Avatar answered Sep 04 '25 04:09

Derviş Kayımbaşıoğlu


Other answers are all correct, but do not address your question if SaveChangesAsync is atomic or not.
According to documentations, the answer to your question is YES, for both EF6 and 'EF Core`:

Ef Core docs:

By default, if the database provider supports transactions, all changes in a single call to SaveChanges() are applied in a transaction. If any of the changes fail, then the transaction is rolled back and none of the changes are applied to the database. This means that SaveChanges() is guaranteed to either completely succeed, or leave the database unmodified if an error occurs.

EF6 docs :

In all versions of Entity Framework, whenever you execute SaveChanges() to insert, update or delete on the database the framework will wrap that operation in a transaction. This transaction lasts only long enough to execute the operation and then completes. When you execute another such operation a new transaction is started.

like image 38
Shahryar Saljoughi Avatar answered Sep 04 '25 05:09

Shahryar Saljoughi