简体   繁体   中英

Transactional SaveChangesAsync in EF

I want to save changes to a list of entities Person in 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;
            }
        }

    }

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;
}
tasks.Add(await MyContext.SaveChangesAsync(););

should be without await

tasks.Add(MyContext.SaveChangesAsync());

And you code becomes working. (await expression returns async result, without await returns Task that will be executed later) But as was mentioned before you should not execute SaveChangesAsync() each time as entity is added to context it might be done for all added entities for one time.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM