Home   |  Articles   |  Resources   |  Humor   |  Feedback       

  Login   Register 

Ads Via DevMavens


Working With HttpContext.Current

Posted by on Sunday, March 21, 2004

The HttpContext.Current property can give you the context for the current request from anywhere in the application domain. This article takes a look at how HttpContext.Current works, and the implications for ASP.NET applications using multiple threads.

The static property Current on the HttpContext class can be useful whenever the flow of control leaves the code in your Page derived web form. Using this property you can reach out and magically grab the current Request, Response, Session, and Application objects (and more) for the request you are servicing. Take the following code as an example.

private void Page_Load(object sender, System.EventArgs e)
{
   MyClass myClass = new MyClass();
   myClass.DoFoo();
}

And - in some other assembly…

class MyClass
{
   public void DoFoo()
   {
      HttpContext.Current.Response.Write("Doing Foo");
   }
}

The ability to grab the context for the current request from any code inside the same application domain is powerful, but can also be misused. You can break the boundaries of your architectural layers using HttpContext.Current from a business object, and easily tie classes to ASP.NET that would otherwise work without change in Windows Forms or on a PDA with the Compact Framework.

The curious among us will wonder just how HttpContext.Current can find the context for the current request. In addition, does it always find the current request? For example, what is the behavior in the following code?

private void Page_Load(object sender, System.EventArgs e)
{
   ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));
}

public void DoWork(object state)
{
   HttpContext context = HttpContext.Current;
  context.Response.Write("Do Work");
}

Answer: the above code generates a System.NullReferenceException because HttpContext.Current returns null. From a design point of view there are at least two problems in the above code, but let’s see how HttpContext.Current works before we discuss them.

A quick peek with a decompiler shows the implementation for the Current property looks something like the following.

public static HttpContext get_Current()
{ 
    return (CallContext.GetData("HtCt") as HttpContext); 
}

The CallContext provides a service extremely similar to thread local storage (except CallContext can perform some additional magic during a remoting call). Thread local storage is a concept where each logical thread in an application domain has a unique data slot to keep data specific to itself. Threads do not share the data, and one thread cannot modify the data local to a different thread. ASP.NET, after selecting a thread to execute an incoming request, stores a reference to the current request context in the thread’s local storage. Now, no matter where the thread goes while executing (a business object, a data access object), the context is nearby and easily retrieved.

Knowing the above we can state the following: if, while processing a request, execution moves to a different thread (via QueueUserWorkItem, or an asynchronous delegate, as two examples), HttpContext.Current will not know how to retrieve the current context, and will return null. You might think one way around the problem would be to pass a reference to the worker thread, like the following example.

private void Page_Load(object sender, System.EventArgs e) 
{ 
    WorkerClass2 worker = new WorkerClass2(); 
    ThreadPool.QueueUserWorkItem(new WaitCallback(worker.DoWork), HttpContext.Current); 
} 

//… 

class WorkerClass2 
{ 
    public void DoWork(object state) 
    { 
        HttpContext context = state as HttpContext; 
        Thread.Sleep(15000); 
        context.Response.Write("Request.Url = " + context.Request.Url); 
    } 
}

However, in my environment the above code also throws an exception, albeit this time from the depths of mscoree. Both the above code and the first example with QueueUserWorkItem suffer from another flaw: both can outlive the lifetime of the page request, and also the valid lifetime of the HttpContext object assigned to that page request. While we can keep a reference to the HttpContext of any request to prevent the garbage collector from taking it, the ASP.NET runtime is certainly free to clear some of the resources as soon as the page request has finished processing. I don’t believe there is anyway to say exactly what will happen with the above code, on some machines in different circumstances the code may actually work, but the chance of failure certainly exists and the condition should be avoided.

There are ways to guarantee the page request does not finish until the worker thread completes its work, for example, the following code.

private void Page_Load(object sender, System.EventArgs e)
{
   WorkerClass worker = new WorkerClass(_resetEvent);
   ThreadPool.QueueUserWorkItem(new WaitCallback(worker.DoWork),
                                HttpContext.Current);
   try
   {
      _resetEvent.WaitOne();
   }
   finally
   {
      _resetEvent.Close();
   }
}
    
AutoResetEvent _resetEvent = new AutoResetEvent(false);

…

class WorkerClass
{
   public WorkerClass(AutoResetEvent resetEvent)
   {  
      _resetEvent = resetEvent;
   }

   public void DoWork(object state)
   {
      try
      {
         HttpContext context = state as HttpContext;
         Thread.Sleep(500);
         context.Response.Write("Do work");
      }
      finally
      {
         _resetEvent.Set();
      }
   }

   AutoResetEvent _resetEvent = null;
}

The above code functions correctly and “Do work” appears in the browser. However, there are still some design points to ponder. First, the ASP.NET runtime processes multiple requests using a finite number of threads. We have just completed the same amount of work, but we’ve doubled the number of threads required, generated additional context switches, and now have a synchronization primitive to manage. If you can perform additional work in the original thread while waiting for the worker task to finish, then there might be a benefit. Generally, however, you should approach the idea of using additional threads in ASP.NET with a certain amount of reservation.

In this article we’ve gained some insight into how HttpContext.Current works, and seen some scenarios where we need to exercise caution. Don’t abuse the power HttpContext.Current gives you, and examine your design and architecture whenever the call appears inside your code.

 

Update: Fritz Onion has an excellent article on MSDN: Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code.

 

-- by K. Scott Allen

Additional Resources

A Matter of Context 

Practical Multithreading for Client Apps


Copyright 2004 OdeToCode.com 


The Blogs
Subscribe to the OdeToCode blogs for the latest news, downloads, new articles, and quirky commentary.
New Articles
Databinding in Silverlight
This article will cover data binding features in Silverlight, including binding expressions, validation, converters, and binding modes.

The Standard LINQ Operators
This article will cover the standard LINQ operators provided by LINQ for filtering, grouping, joining, converting, projecting, and more.

C# 3.0 and LINQ
C# 3.0 introduced a number of new features for LINQ. In this article we'll examine the new features like extension methods, lambda expressions, anonymous types, and more.

Most Popular Articles
Table Variables In T-SQL
Table variables allow you to store a resultset in SQL Server without the overhead of declaring and cleaning up a temporary table. In this article, we will highlight the features and advantages of the table variable data type.

ASP.Net 2.0 - Master Pages: Tips, Tricks, and Traps
MasterPages are a great addition to the ASP.NET 2.0 feature set, but are not without their quirks. This article will highlight the common problems developers face with master pages, and provide tips and tricks to use master pages to their fullest potential.

AppSettings In web.config
In this article we will review a couple of pratices to keep your runtime configuration information flexible.

Contribute Code
Privacy
Consultancy