Richa
Richa

Reputation: 3289

HttpContext.Current.Session null after making method async

I had a method like below

book.Bindbook();

I made it async as follow

new Task(book.Bindbook).Start();

Now this method uses HttpContext.Current.Session which is now returning null. Here is code that returns null

public static Bookmanager CartManager
{
    //Gets the value from the session variable.
    get
    {
        try
        {
            if (HttpContext.Current.Session["BookData"] == null)
            {
                Bookmanager bookmgr= new Bookmanager ();
                Book book = new Book(SessionManager.CurrentUser);
                bookmgr.SetCurrentCart(book);                       
                HttpContext.Current.Session["BookData"] = bookmgr;
            }
            else if (((Bookmanager)HttpContext.Current.Session["BookData"]).GetCurrentCart() == null)
            {
                Book book = new Book(SessionManager.CurrentUser);
                ((Bookmanager)HttpContext.Current.Session["BookData"]).SetCurrentCart(book);
            }
        }
        catch(Exception ex)
        {
            //throw ex;
        }
        return ((Bookmanager)HttpContext.Current.Session["BookData"]);
    }
    //Sets the value of the session variable.
    set
    {
        HttpContext.Current.Session["BookData"] = value;
    }
}

Upvotes: 3

Views: 4968

Answers (3)

HarryPotter
HarryPotter

Reputation: 119

Use ConfigureAwait(true) to allow to continue on the original context.

var task = new Task(() => book.Bindbook()).ConfigureAwait(true);

task.Start();

Upvotes: 1

Paul Turner
Paul Turner

Reputation: 39615

There's a lot of potential problems with your solution which have lead to this problem. I'll try to break it down into pieces to explain what's going on.

new Task(book.Bindbook).Start() doesn't always run where you think it does

This method of creating an asynchronous operation is subtly dangerous as it's not easy to know how the task will be executed. When you call this constructor, the Task will capture the TaskScheduler.Current value as the mechanism it will use to schedule it's own execution. This means that your task's execution is invisibly tied to the context it's in.

Typically, you want to use Task.Run(Action) instead of creating a new Task instance and then calling Start, as this always runs on the value of TaskScheduler.Default, which is usually the .NET thread pool and is generally what you want to do when running a background task.

HttpContext is not thread-safe

The HttpContext class was never intended to be called from multiple threads safely. It's Current value tied to the thread which is processing the request and is not available on other threads. You should not pass it to other threads. Generally-speaking you should reduce the surface-area of HttpContext in your applications to a bare minimum. It's nearly impossible to mock for testing purposes and has several subtle limitations (such as you are finding) which make it challenging to work with.

Instead, surface the Current value as early as possible in your code and keep a reference to the objects you actually need to work with (like the session).

Static properties are usually harmful

Having a static property on an object either means that there are exactly one of these things for the entirety of the AppDomain (such as TaskScheduler.Default) where they represent some cross-cutting concern that can be configured, or that there is some hidden context manipulating the value behind the scenes. The former case is rare, but can be acceptable in some cases, but the second is pretty harmful. HttpContext.Current is an example of a value that should not be static (and future version of ASP.NET do away with it entirely). It makes code hard to reason about, nearly impossible to test and introduces subtle bugs (like this one) which can't easily be dealt with.

Fundamentally, this is the biggest problem here and the root cause of your pain. If this property were exposed as an instance property and the instance was scoped to the request context, you would have none of your issues. Once you're working with an object whose lifetime is the same as your request, all your critical state becomes local and easy to reason about.

Upvotes: 5

Timur Mannapov
Timur Mannapov

Reputation: 217

HttpContext is bound to thread, that's why it is null. I think better solution will be to pass all needed data through parameters to other thread and not sharing HttpContext.

Upvotes: 0

Related Questions