HttpContext.Current null inside async task
Asked Answered
B

1

12

I have a method that uses a repository (userRepo):

    public override Task<IdentityResult> CreateLocalUserAsync(IUser user, string password, CancellationToken cancellationToken)
    {
        var task = new Task<IdentityResult>(() => {

            TUserEntity newUser = new TUserEntity
            {
                Id = user.Id,
                UserName = user.UserName,
                Password = password
            };

            userRepo.Save(newUser).Flush();

            return new IdentityResult(true);
        }, cancellationToken);

        task.Start();

        return task;
    }

The userRepoobject has a dependency that uses HttpContext.Current. Both of these are resolved using ninject InRequestScope.

The above method is called inside the default AccountController in Mvc 5:

var result = await IdentityManager.Users.CreateLocalUserAsync(user, model.Password);

I have tried adding this setting to web.config:

<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />

Also, I am definitely using .NET 4.5. This is also in my web.config:

<httpRuntime targetFramework="4.5" />

It is not possible to get the information from the HttpContext before I start the task because a dependency of the userRepo in the task is using the information and both objects are resolved using Ninject.

How can I ensure that HttpContext.Current will not be null?

Braze answered 1/10, 2013 at 8:17 Comment(3)
“How can I ensure that HttpContext.Current will not be null?” Don't use Tasks or async-await at all. It doesn't improve anything in this case.Kyd
@Kyd well, await is http-context aware, at least; the problem here is that Task.Start isn't.Classy
Link: #18384423Tersanctus
C
17

The "task friendly sync context" here applies to the continuation from the await: whatever you do with result, it will have the http-context. It does not, however, relate to task.Start. That relates to the TaskScheduler, not the sync-context.

Basically, by performing this on a worker, you are (in the process, as a consequence) divorcing that worker from the http-context. You must either:

  • get the information you need from the http-context and pass that into the worker, or
  • don't use a worker

Personally, I doubt you're gaining much by pushing this onto a worker. If you really want to go async, the ideal would be for your repo to internally support *Async methods. That requires more than using threads: it usually means architectural changes, for example, using async SQL methods. Something written from the ground up to use async and sync-context-aware continuations (aka await) would automatically preserve things like the http-context.

The important difference here is that an async/await implementation is linear but not continuous, i.e.

 <===(work)==>
                    <===(callback; more work)===>
                                                     <===(another callback)===>

where-as your existing code potentially performs things in parallel, i.e.

<==========(original work)=================>
         <===========(task on worker thread)=============>

The fact that the async/await approach is basically linear makes it far more suitable for access to things like http-context, since it knows that (done right) there will only be one thread at a time accessing it - even if it isn't the same thread end-to-end.

Classy answered 1/10, 2013 at 9:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.