user3818229 February 2016

Wrap async lambda to the async method

I'm trying to wrap my Operations Contracts to the try-catch block. The problem is that my EF context destroyed in the same time scope. I guess the problem compiler doesn't know what to do properly. Also my catch block doesn't handle any exceptions. Sorry for my English, I hope my code show what I mean:

private static async Task<T> SurroundWithTryCatch<T>(Action t, T response) where T : BaseResponse 
{
    try
    {
        await Task.Run(t);
    }
    catch (Exception e)
    {
        response.Success = false;
        response.Errors.Add(e.Message);
    }
    return response;
}

public async Task<CreateResponse> CreateAsync(CreateRequest request)
{
    var response = new CreateResponse();
    return await SurroundWithTryCatch(async delegate
    {
        var newUser = new ApplicationUser { UserName = request.UserName, Email = request.Email };
        await Database.UserManager.CreateAsync(newUser, request.Password);
        //Some another logic...
        await Database.CommitAsync();
    }, response);
}

The problem in the second line of CreateAsync method. UserManager was cleaned by GC earlier. So I have ObjectDisposedException. Database is the IUnitOfWork implementation and injected by Autofac.

Answers


Luaan February 2016

You're breaking the await chain - t no longer returns a task. Since you no longer have a task, the execution will continue after the first await, rather than after the whole method is done. Think of await as return - if you don't return (and await/wait for) a Task, you lose your chance at synchronization.

Instead, you want to pass Func<Task>, and await it directly:

private static async Task<T> SurroundWithTryCatch<T>(Func<Task> t, T response) where T : BaseResponse 
{
    try
    {
        await t();
    }
    catch (Exception e)
    {
        response.Success = false;
        response.Errors.Add(e.Message);
    }
    return response;
}

public async Task<CreateResponse> CreateAsync(CreateRequest request)
{
    var response = new CreateResponse();
    return await SurroundWithTryCatch(async () =>
    {
        var newUser = new ApplicationUser { UserName = request.UserName, Email = request.Email };
        await Database.UserManager.CreateAsync(newUser, request.Password);
        //Some another logic...
        await Database.CommitAsync();
    }, response);
}

Task.Run also works, but you probably don't want that anyway - your code is asynchronous, and (a guess) you're running in a ASP.NET request, so there's no benefit to launching a task just to wait on the result of another task. Task.Run is used for doing CPU work in a separate thread, something you usually want to avoid in ASP.NET.

Post Status

Asked in February 2016
Viewed 2,401 times
Voted 4
Answered 1 times

Search




Leave an answer