Search code examples
asp.netasync-awaitaspnetboilerplatesqlexceptionusing-statement

Why does _session.Use method not work properly?


I have a job like this:

[UnitOfWork]
public override void Execute(CompleteIRHJobArgs args)
{
    var robotUserId = _userRepo.GetAll().Where(p => p.UserName == TestaLIMSWPConsts.LIMSRobot).Select(p => p.Id).First();

    using (_session.Use(args.TenantId, robotUserId))
    {
        _instanceReciptHeaderDomainService.SetIRHToCompleteState(args.IRHIds);
    }
}

I find robotUserId and set it as the current user. But after I step into method SetIRHToCompleteState, _session.UserId.Value is null. I think it is wrong behavior. My ABP version is 4.0.0.

public async Task SetIRHToCompleteState(List<int> irhIds)
{
    var irhs = await _instanceHeaderRepo.GetAll().Where(p => irhIds.Contains(p.Id)).ToListAsync();

    foreach (var t in irhs)
    {
        t.FlowState = FlowState.Completed;
        t.CompleteDate = Clock.Now;
        t.CompleteUserId = _session.UserId.Value;
    }
}

And sometimes,

var irhs = await _instanceHeaderRepo.GetAll()...

throws exception:

System.Transactions.TransactionInDoubtException: The transaction is in doubt. ---> System.Data.SqlClient.SqlException: There is already an open DataReader associated with this Command which must be closed first. ---> System.ComponentModel.Win32Exception: The wait operation timed out


Solution

  • But after step into method SetIRHToCompleteState, _session.UserId.Value is null.

    SetIRHToCompleteState is async and continued running after the using scope was disposed.

    Since Execute is not async, you cannot await but you can call AsyncHelper.RunSync instead.

    // using Abp.Threading;
    
    using (_session.Use(args.TenantId, robotUserId))
    {
        AsyncHelper.RunSync(() => _instanceReciptHeaderDomainService.SetIRHToCompleteState(args.IRHIds));
    }
    

    This would also avoid the "open DataReader" error.


    From aspnetboilerplate/aspnetboilerplate#1646:

    it's called in a background thread which is not inside an async context. But it's not a problem since background job manager is already single threaded and does not cause to block many threads.
    Hangfire implementation is also like that.