Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Start a task with clean AsyncLocal state

In ASP.NET Core, I am using IHttpContextAccessor to access the current HttpContext. HttpContextAccessor uses AsyncLocal<T>.

In one situation, I am trying to start a Task from within a request, that should continue running after the request has ended (long running, background task), and I am trying to detach it from the current execution context so that IHttpContextAccessor returns null (otherwise I am accessing an invalid HttpContext).

I tried Task.Run, Thread.Start, but every time, the context seems to carry over and IHttpContextAccessor returns the old context whatever I try (sometimes even contexts from other requests 🤔).

How can I start a task that will have a clean AsyncLocal state so that IHttpContextAccessor returns null?

like image 809
Flavien Avatar asked Jun 26 '17 10:06

Flavien


2 Answers

Late answer but might be useful for someone else. You can do this by suppressing the ExecutionContext flow as you submit your task:

ExecutionContext.SuppressFlow();

var task = Task.Run(async () => {
    await LongRunningMethodAsync();
});

ExecutionContext.RestoreFlow();

or better:

using (ExecutionContext.SuppressFlow()) {
    var task = Task.Run(async () => {
        await LongRunningMethodAsync();
    });
}

This will prevent the ExecutionContext being captured in the submitted task. Therefore it will have a 'clean' AsyncLocal state.

As above though I wouldn't do this in ASP.net if it's CPU intensive background work.

like image 177
miroslav22 Avatar answered Sep 23 '22 05:09

miroslav22


You could await the long running task and set HttpContext to null inside await. It would be safe for all code outside the task.

[Fact]
public async Task AsyncLocalTest()
{
    _accessor = new HttpContextAccessor();

    _accessor.HttpContext = new DefaultHttpContext();

    await LongRunningTaskAsync();

    Assert.True(_accessor.HttpContext != null);
}

private async Task LongRunningTaskAsync()
{
    _accessor.HttpContext = null;
}

You could run a separate thread, it doesn't matter.

Task.Run(async () => await LongRunningTaskAsync());

Just make the task awaitable to cleanup HttpContext for the task's async context only.

like image 45
Ilya Chumakov Avatar answered Sep 19 '22 05:09

Ilya Chumakov