Access HttpContext.Current from different threads
Asked Answered
M

5

58

I have a C# ASP.NET application which starts about 25 different threads running some methods in a class called SiteCrawler.cs.

In HttpContext.Current.Session I want to save the result of the search made by the user and present it to the user when all the threads are finished running. My problem is that the HttpContext.Current object is null in the spawned threads because it doesn't exist there.

What other options do I have to save user/session specific data without using session because of the limitations when the application is multithreaded?

I have tried to search around every inch of Stackoverflow to find a solution but without any luck....

Mesonephros answered 19/1, 2012 at 11:26 Comment(7)
I guess you could always pass the current HttpContext when you create the new threads and update the session in that onePostimpressionism
Isn't it possible to run the 25 threads, collect the results in the asp.net thread and then store the complete results in your session?Juarez
Spawning threads for 'long running processes' is, by rule of thumb, a bad idea in a web-facing application.Doorplate
musefan - I thought about that, but i'm not sure if it's a good idea, because the spawned thread would change the content of the passed on HttpContext which by the time also would be different.Mesonephros
Wouter de Kort - How would your approach be to achieve this?Mesonephros
I've been looking to solve a related problem, and it seems like the "right" way to flow the HttpContext is to get your hands dirty with the SynchronizationContext. Rather than going into detail here, I'll just point you to this thorough and well-written blog post which address your question about HttpContext, and in more complex threading scenarios too. In it he mentions this classic post by Stephen Toub, [ExecutionContext vs SynchronizationContext](httpCondottiere
@GrantThomas Serious question here: Chrome has a (far too short) timeout on queries that we have been unable to control (it's actually very inconsistent). How then would you suggest structuring a call to a long-running query?Hearken
G
84

In my application there are a lot of code that uses HttpContext.Current and I can not modify that code.

worker.DoWork() from sample below uses that code. And I had to run it in separate thread.

I came to the following solution:

 HttpContext ctx = HttpContext.Current;
 Thread t = new Thread(new ThreadStart(() =>
                {
                    HttpContext.Current = ctx;
                    worker.DoWork();
                }));
 t.Start();
 // [... do other job ...]
 t.Join();
Guinn answered 11/10, 2013 at 13:19 Comment(9)
wow thanks you saved me! i wonder how this hits that memory if i use it moreSabrinasabsay
Note that in general this is a bad thing to do. For example, sometimes you may find that HttpContext.Current.Items[x] returns null instead of the value you put there, because ASP has cleaned up resources after the http request has finished. This is a quick & dirty approach that probably will work most of the time but don't rely on it for anything important.Artel
I disagree with @Artel 's point. I think its the most elegant solution for this by far when you compare it with other options. First of all you're not cloning HttpContext but instead just passing a reference. As Thread may run longer than HttpRequest itself one could easily check if HttpContext is null prior to accessing it. As an improvement to above code snippet passing "ctx" could also be done via WeakReference - this way the thread won't prevent HttpContext from being GC'ed if needed. Again depending on the life-cycle you need you adjust the code above to fit your needs. + Accepted AnswerPeriosteum
@Artel that implies a fire-and-forget async call, and that is a bad idea. If you don't do anything funky and make sure that the web request waits for all async calls to finish, you should be fine, right?Secure
I think I hadn't noticed the t.Join() call on this answer when I wrote my previous comment. I agree it should be fine if the background threads finish before the http request. In my experience the problems have occurred when the web request finishes first and the background task is still running. Note the problem is NOT that the HttpContext gets gc-ed - clearly there's still a reference so it won't be - but that something in ASP.NET deletes all the elements in HttpContext.Current, so then if you expect anything to be in that collection you're out of luck.Artel
@MinhNguyen did you need to access the HttpContext.Current.Session in your case? Using this approach, I can receive the HttpContext.Current but not the HttpContext.Current.SessionRoadwork
helped me to use parallel library in asp.net application with session.Concavoconvex
How can I set HttpContext.Current if I am injecting HttpContextBase into my class?Blackandwhite
I faced a similar restriction and used FakeHttpContext which is serialized (I capture all session variables) and then restored into this FakeHttpContext (like this oneVeasey
C
11

Have a look at this article by Fritz Onion: Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code. It's quite long, but your requirement is not too trivial.

Also K. Scott Allen posted a somewhat shorter article about this very issue: Working With HttpContext.Current

Castro answered 19/1, 2012 at 11:32 Comment(1)
That article 'Working with HttpContext.Current' was vital in assisting me with my particular gotcha, thanks.Crossness
B
7

@Rory made a comment above, that certain objects in the HttpContext will become null even if you pass it into the Thread. This happened to me with the User property. So instead you can copy the User to the thread CurrentPrincipal like this:

In the controller context, save off the user:

            _user = HttpContext.Current.User;
            var processThread = new Thread(() => ThreadedCode());
            processThread.Start();

In the thread, set the 'Thread's' user:

    private static void ThreadedCode()
    {
        // Workaround for HttpContext.Current.User being null.
        // Needed for CreatedBy and RevisedBy.
        Thread.CurrentPrincipal = _user;

Note that the HttpContext will only be available for the lifetime of the request. The thread will live on potentially much longer than the request, which is probably why you need a thread in the first place! :)

Brena answered 23/2, 2016 at 22:5 Comment(1)
@Artel thanks for your comment above. It got me thinking about this alternate answer.Brena
A
5

Just add the HttpContext.Current to the constructor of your class SiteCrawler.cs

public class SiteCrawler
{
     HttpContext context = HttpContext.Current;

    public void Method()
    {
        context.WhateverYouWant
    }
}
Ajar answered 17/2, 2015 at 23:54 Comment(0)
A
3

You can save it to the database and then, you can let the user's browser to keep refreshing or using ajax or using the new signalr to check if the result is already written in db. hope it helps.

Abisha answered 19/1, 2012 at 11:30 Comment(1)
Saving the session variables to a database wouldn't be a problem. I just don't know what the best way would be to bind it to the correct session and retrieve it when the threads are done.Mesonephros

© 2022 - 2024 — McMap. All rights reserved.