Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework 6.1.0 SaveChangesAsync

I have EF helper class that saves changes async:

public async Task<int> SaveOrUpdateAsync<TEntity>(TEntity entity)
        where TEntity : class, IContextEntity
    {
        if (entity.Id == 0)
            context.Set<TEntity>().Add(entity);
        else
        {
            TEntity dbEntry = context.Set<TEntity>().Find(entity.Id);
            if (dbEntry != null) dbEntry = entity;
        }

        return await context.SaveChangesAsync();
    }

public void Save()
{
Task saveEntit1Async = repository.SaveOrUpdateAsync<Entity1>(entity1);
Task saveEntity2Async = repository.SaveOrUpdateAsync<Entity2>(entity2);
Task saveEntity3Async =  repository.SaveOrUpdateAsync<Entity3>(Entity3);

Task.WaitAll(saveEntit1Async, saveEntity2Async, saveEntity3Async);

string test = "test";
)

The call gets stuck on

Task.WaitAll(saveEntit1Async, saveEntity2Async, saveEntity3Async);

line and never gets to

 string test = "test";

But if I run it as:

public void Save()
{
repository.SaveOrUpdateAsync<Entity1>(entity1);
repository.SaveOrUpdateAsync<Entity2>(entity2);
repository.SaveOrUpdateAsync<Entity3>(Entity3);

string test = "test";
)

It works fine, all changes are being saved and it gets to

string test = "test";

Why is

Task.WaitAll(saveEntit1Async, saveEntity2Async, saveEntity3Async);

Freezes up the operation and never passes call to the next line of code (string test = "test";) ?

like image 948
Alexander C. Avatar asked Apr 04 '14 16:04

Alexander C.


Video Answer


1 Answers

I figured it out!

Here is the problem that was happening, when, you wait on the Task with the "Wait" method or take the result directly from the "Result" property of the Task, you block the main thread at the same time. When eventually the Task completes inside that method (SaveOrUpdateAsync(TEntity entity)) in the thread pool, it is going to invoke the continuation to post back to the main thread (as it never left it), because SynchronizationContext.Current is available and captured. But here is a problem: the main thread is blocked by "Wait" method and that is how I was getting a deadlock!

To fix deadlock issue I had to specify to not to continue on captured context for context.SaveChangesAsync().

public async Task<int> SaveOrUpdateAsync<TEntity>(TEntity entity)
        where TEntity : class, IContextEntity
    {
        if (entity.Id == 0)
            context.Set<TEntity>().Add(entity);
        else
        {
            TEntity dbEntry = context.Set<TEntity>().Find(entity.Id);
            if (dbEntry != null) dbEntry = entity;
        }

        return await context.SaveChangesAsync().ConfigureAwait(continueOnCapturedContext: false);
    }
like image 171
Alexander C. Avatar answered Sep 20 '22 18:09

Alexander C.