Search code examples
c#ninjectasync-awaithttpcontext

HttpContext.Current null inside async task


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?


Solution

  • 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.