Search code examples
c#asynchronous.net-coreasync-awaitidisposable

Return a Func<Task> middle of Using block


I want to return a Func middle of a using block. Should I worry about disposing of, before the user runs the result Func?

A sample code:

private IDbContextTransaction _transaction;

public Func<Task> BeginTransaction()
{
    Task lockDispose = CommitTransaction();
    using (_transaction = _dbContext.Database.BeginTransaction())
    {
        return async() =>
        {
            await lockDispose;
        };
        Task.WaitAll(lockDispose); //This code is unreachable.
    }
}

private async Task CommitTransaction()
{
    _transaction.Commit();
    await Task.CompletedTask;
}

Note that the execution time of the result Func is up to the user of this service.

I checked This Question and it's not my answer.


Solution

  • Since you want your user to be able to work with the connection between calling BeginTransaction and CommitTransaction, then the only thing you can do is to move disposal outside of this scope:

    private IDbContextTransaction _transaction;
    
    public Func<Task> BeginTransaction()
    {
        _transaction = _dbContext.Database.BeginTransaction()
        return CommitTransaction;
    }
    
    private async Task CommitTransaction()
    {
        _transaction.Commit();
        _transaction.Dispose();
        await Task.CompletedTask;
    }
    
    // I advice you implement IDisposable fully, but 
    // this will do for the sake of demonstration
    public void Dispose()
    {
        _transaction?.Dispose();
    }
    

    And then somewhere outside you'll need to do something along the lines of:

    using (var obj = TheWayYouInitializeYourObject())
    {
        var commit = obj.BeginTransaction();
        // DO WORK
        await commit();
    }
    

    or

    try
    {
        var commit = obj.BeginTransaction();
        // DO WORK
        await commit();
    }
    finally
    {
        obj.Dispose();
    }
    

    Basically, your using cannot span the gap in which your user works, so they'll have to do it on their own.

    It is generally a good idea to make objects that have to work with disposables disposable as well.