Queuing BankgroundJob with Hangfire within an async action in ASP.NET MVC freeze the application
Asked Answered
K

0

2

I have this action on one of my controllers which is called by another method which has been called also by another action.

All works fine, unless I try to en-queue with Hangfire some jobs: _bankClient.FetchAndEnsureTransactionsAsync and _bankClient.RefreshItemAsync. This is causing the application to freeze (the client, browser, stops while the server is still running). I suppose is some weird deadlock, but nothing I've tried seems to make it work!

Would someone know how to solve this problem?

I'm aware Hangfire does not support yet calling async methods, so I have the synchronous version of this methods which I en-queue.

NOT working action

private async Task<ActionResult> ActionOnFinished(long itemId, ItemStatus status, Guid userId)
    {
        var seller = await _unitOfWork.CompanyRepository.GetCompanyByUserIdAsync(userId).ConfigureAwait(false);
        await _bankClient.CreateItemAndAccountsAsync(userId, seller.Id, itemId, seller.Name, seller.Siren.ToString()).ConfigureAwait(false);

        // TODO: use hangfire when available and fire this on BackgroundJob
        if (Properties.Settings.Default.EnableHangfire)
        {
            BackgroundJob.Schedule<IBankClientService>(bs => bs.FetchAndEnsureTransactions(userId), DateTimeOffset.Now.AddMinutes(1));
            BackgroundJob.Schedule<IBankClientService>(bs => bs.RefreshItem(userId), DateTimeOffset.Now.AddMinutes(1));
        }
        else
        {
            await _bankClient.FetchAndEnsureTransactionsAsync(userId).ConfigureAwait(false);
            await _bankClient.RefreshItemAsync(userId).ConfigureAwait(false);
        }

        return RedirectToAction(MVC.Dashboard.MyProfile());
    }

Working action :

 private async Task<ActionResult> ActionOnFinished(long itemId, ItemStatus status, Guid userId)
    {
        var seller = await _unitOfWork.CompanyRepository.GetCompanyByUserIdAsync(userId).ConfigureAwait(false);
        await _bankClient.CreateItemAndAccountsAsync(userId, seller.Id, itemId, seller.Name, seller.Siren.ToString()).ConfigureAwait(false);

        // TODO: use hangfire when available and fire this on BackgroundJob
        await _bankClient.FetchAndEnsureTransactionsAsync(userId).ConfigureAwait(false);
        await _bankClient.RefreshItemAsync(userId).ConfigureAwait(false);

        return RedirectToAction(MVC.Dashboard.MyProfile());
    }

The precedent action is called by a private method:

private async Task<ActionResult> SelectActionByStatus(long itemId, ItemStatus status, Guid userId)
    {
        try
        {                
            if (status.Status == RefreshStatusValues.Finished)
            {
                return await ActionOnFinished(itemId, status, userId).ConfigureAwait(false);
            }

            return ActionOnError();
        }
        catch (Exception ex)
        {
            _logger.Error(ex, nameof(SelectBank));
            throw new ValidationFailureException(nameof(GetAccounts), ex.Message);
        }
    }

This action has been called by another action:

public async virtual Task<ActionResult> CallBack(string item_id)
    {
        try
        {
            var itemId = long.Parse(item_id);

            var userId = CurrentUserId.Value;
            // as this action is being called by a child window whom is being closed we need to configure the awaiter with ConfigureAwait(false)
            var status = await _bankClient.CheckItemStatusAsync(userId, itemId).ConfigureAwait(false);

            return await SelectActionByStatus(itemId, status, userId).ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            _logger.Error(ex, nameof(CallBack));
            return RedirectToAction(MVC.Bank.BankError());
        }
    }

SYNCHRONOUS VERSION: being enqueued:

public IEnumerable<Transaction> FetchAndEnsureTransactions(Guid userId)
    {
        try
        {
            return FetchAndEnsureTransactionsAsync(userId).ConfigureAwait(false).GetAwaiter().GetResult();
        }
        catch (Exception ex)
        {
            _logger.Error(ex, $"{nameof(FetchAndEnsureTransactions)}");
            throw;
        }
    }

public void RefreshItem(Guid userId)
    {
        try
        {
            RefreshItemAsync(userId).ConfigureAwait(false).GetAwaiter().GetResult();
        }
        catch (Exception ex)
        {
            _logger.Error(ex, $"{nameof(RefreshItem)}");
            throw;
        }
    }
Kwa answered 14/9, 2016 at 8:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.