Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UOW - A second operation started on this context before a previous asynchronous operation completed

I am trying following code, it has two parts, one is navigation via prism. When navigation is allowed I am starting a deep load asynchronously but each time with a new context. In later code I would want to cancel pending navigations that are not finished this loading but the below code does not even work so cancellation is a matter for later ;-)

navigation logic : no problems here

public void OnNavigatedTo(NavigationContext navigationContext)
{
    int relatieId = (int)navigationContext.Parameters["RelatieId"];
    if (_relatie != null && _relatie.RelatieId == relatieId) return;

    loadRelatieAsync(relatieId);
}

public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
    bool navigationAllowed = true;
    continuationCallback(navigationAllowed);
}

deep loading logic:

private async Task loadRelatieAsync(int relatieId)
{
    try
    {
        await Task.Run(async () =>
        {

            _unitOfWork = _UnitOfWorkFactory.createUnitOfWorkAsync();

            IEnumerable<Relatie> relaties = await getRelatieAsync(_unitOfWork, relatieId).ConfigureAwait(true);

            _relatieTypeTypes = await getRelatieTypeTypesAsync(_unitOfWork, relatieId).ConfigureAwait(true);
            _relatie = relaties.FirstOrDefault();

            _unitOfWork.Dispose();
        }).ConfigureAwait(true);

        processRelatie(_relatie);

        processRelatieTypes(_relatie, _relatieTypeTypes);
    }
    catch (Exception Ex)
    {

        MessageBox.Show(Ex.Message);
        throw;
    }

}

private async Task<IEnumerable<Relatie>> getRelatieAsync(IUnitOfWorkAsync unitOfWork, int relatieId)
{

    IEnumerable<Relatie> relaties = null;
    try
    {
        IRepositoryAsync<Relatie> relatieRepository = unitOfWork.RepositoryAsync<Relatie>();
        relaties = await relatieRepository
            .Query(r => r.RelatieId == relatieId)
            .Include(i => i.BegrafenisOndernemer)
            .SelectAsync()
            .ConfigureAwait(false);

        IRepositoryAsync<Adres> adresRepository = unitOfWork.RepositoryAsync<Adres>();
        //exception is thrown after executing following line
        var adressen = await adresRepository
            .Query(r => r.RelatieId == relatieId)
            .Include(i => i.AdresType)
            .SelectAsync()
            .ConfigureAwait(false);
        _relatieTypeRepository = unitOfWork.RepositoryAsync<RelatieType>();
        var relatieTypes = await _relatieTypeRepository
            .Query(r => r.RelatieId == relatieId)
            .SelectAsync()
            .ConfigureAwait(false);
    }
    catch (Exception Ex)
    {
        MessageBox.Show(Ex.Message);//exception is shown here
        throw;
    }
    return relaties;
}

private async Task<IEnumerable<RelatieTypeType>> getRelatieTypeTypesAsync(IUnitOfWorkAsync unitOfWork, int relatieId)
{

    IEnumerable<RelatieTypeType> relatieTypeTypes = null;
    try
    {
        IRepositoryAsync<RelatieTypeType> relatieTypeTypeRepository =
            unitOfWork.RepositoryAsync<RelatieTypeType>();

        relatieTypeTypes = await relatieTypeTypeRepository
            .Query()
            .SelectAsync()
            .ConfigureAwait(false);

    }
    catch (Exception Ex)
    {
        MessageBox.Show(Ex.Message);
        throw;
    }
    return relatieTypeTypes;
}

I keep getting exceptions as if I forgot to await, but this is never the case. I also use configureawait(true) properly whenever I want continuation on the GUI thread. But I keep getting this error in the deeploading logic. The unit of work and repository classes also use the async await mechanism but also there I am awaiting properly.

A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.

Edit (removed logger code to reduce the code size)

like image 427
Philip Stuyck Avatar asked Feb 04 '15 20:02

Philip Stuyck


Video Answer


2 Answers

The problem is this code :

_unitOfWork = _UnitOfWorkFactory.createUnitOfWorkAsync();

IEnumerable<Relatie> relaties = await getRelatieAsync(_unitOfWork, relatieId).ConfigureAwait(true);

_relatieTypeTypes = await getRelatieTypeTypesAsync(_unitOfWork, relatieId).ConfigureAwait(true);
_relatie = relaties.FirstOrDefault();

 _unitOfWork.Dispose();

the UnitOfWork is an instance variable, when the scenario starts a second time it is overwritten with a new instance. The already executing scenario then uses that new UnitOfWork instead of its own because it is overwritten. Not so easy to spot, but a simple race condition. I found it by replacing all instance variables by local variables and then the problem disappeared.

like image 109
Philip Stuyck Avatar answered Oct 23 '22 12:10

Philip Stuyck


This is probably not the answer, but a general look over your code.

Main purpose of async/await is, to keep the current Thread available. This helps not blocking the UI Thread and keeping your App responsive.

You are already making sure that your deep loading happens on a ThreadPool thread, because you are starting it entirely utilizing Task.Run(). You can probably get around most of your problems by using the default synchronous methods of EntityFramework in your loading mechanism.

In a first look, your code looks fine as of the async calls. Maybe your deep loading is triggered multiple times?

like image 30
Kai Brummund Avatar answered Oct 23 '22 13:10

Kai Brummund