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?

svick
  • 236,525
  • 50
  • 385
  • 514
orourkedd
  • 6,201
  • 5
  • 43
  • 66
  • “How can I ensure that `HttpContext.Current` will not be null?” Don't use `Task`s or `async`-`await` at all. It doesn't improve anything in this case. – svick Oct 01 '13 at 09:00
  • @svick well, `await` is http-context aware, at least; the problem here is that `Task.Start` isn't. – Marc Gravell Oct 01 '13 at 09:19
  • Link: http://stackoverflow.com/questions/18383923/why-is-httpcontext-current-null-after-await – cuongle Sep 27 '16 at 14:33

1 Answers1

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900