Avoid static variables in ASP.NET

Add to FacebookAdd to DiggAdd to Del.icio.usAdd to StumbleuponAdd to RedditAdd to BlinklistAdd to TwitterAdd to TechnoratiAdd to Yahoo BuzzAdd to Newsvine

Occasionally, I like to write static methods on classes. They are useful whenever the method loosely relates to to the class – but it doesn’t involve a specific instance of the class.

A good example of a static method in the .NET library is the int.Parse(string val) method. This method basically reads a string and tries to return an integer representation. The method logically fits with the int Class (I suppose a string could have a ToInt() instance method…but that would be overwhelming as you added ToFloat(), ToDecimal(), To…()) – but when you run it, there’s no reason to have an instance of int already. You run it to create a new instance of int.

Commonly, I follow similar logic in the business layer of my webapps. I write a static Add(param1,….,paramx) method that returns an instance of the class after it is persisted to the database. I could accomplish this other ways, but I think the resulting application code reads better like:

      User.Add(username,password);

Than:

      new User(username,password).Add();

or, worse:

      DataContext.Users.InsertOnSubmit(new User(username,password));
      DataContext.SubmitChanges();

The issue with static methods in your business logic is that you often need a common object to talk to the database through: a SqlConnection, a TableAdapter, A LINQ DataContext, etc. I could certainly create those objects locally inside each static method, but that’s time consuming and hard to maintain. I want instead to define a common property (in the business class or a parent class of it) that lazy-initializes and returns an instance of the object when I need it. The problem is that the property must also be static for a static method to have access to it.

The easiest way to accomplish this is something like:

      private static ModelDataContext dataContext=null;
      protected static ModelDataContext DataContext
      {
            get
            {
                 if(dataContext==null)
                     dataContext = new ModelDataContext();
                 return dataContext;
             }
       }

The tricky thing is that this will probably work in development and testing, until you get some load on your website. Then, you’ll start seeing all kinds of weird issues that may not even point to this code as the problem.

Why is this an issue? It’s all about how static variables are scoped inside a ASP.NET application. Most web programmers think of each page in their application as its own program. You have to manually share data between pages when you need to. So, they incorrectly assume that static variables are somehow tied to a web request or page in their application. This is totally wrong.

Really, your whole website is a single application, which spawns threads to deal with requests, and requests are dealt with by the code on the appropriate page. Think of a page in your webapp as a method inside of one big application, and not an application of its own – a method which is called by the url your visitor requests.

Why does this matter? Because static variables are not tied to any specific instance of any specific class, they must be created in the entire application’s scope. Effectively, ASP.NET static variables are the same as the global variables that all your programming teachers warned you about.

That means that, for the property above, every single request/page/user of your website will reuse the first created instance of DataContext created. That’s bad for several reasons. LINQ DataContexts cache some of the data and changes you make – you can quickly eat up memory if each instance isn’t disposed fairly quickly. TableAdapters hold open SQLConnections for reuse – so if you use enough classes of TableAdapters, you can have enough different static vars to tie up all of your db connections. Because requests can happen simultaneously, you can also end up with lots of locking/competing accesses to the variable. Etc.

What should you do about it? In my case, I take advantage of static properties that reference collections that are scoped to some appropriately short-lived object for my storage. For instance, System.Web.HttpContext.Current.Items:

      protected static ModelDataContext DataContext
      {
            get
            {
                 if(System.Web.HttpContext.Current.Items["ModelDataContext"]==null)
                     System.Web.HttpContext.Current.Items["ModelDataContext"] = new ModelDataContext();
                 return (ModelDataContext)System.Web.HttpContext.Current.Items["ModelDataContext"];
             }
       }

In this case, each instance of DataContext will automatically be disposed for each hit on the site – and DataContexts will never be shared between two users accessing the site simultaneously. You could also use collections like ViewState, Session, Cache, etc (and I’ve tried several of these). For my purposes, the HttpContext.Items collection scopes my objects for exactly where I want them to be accessible and exactly how long I want them to be alive.

Advertisements

6 Responses to “Avoid static variables in ASP.NET”

  1. iMissClassic Says:

    Thanks! This article explains static var issue in ASP.Net very well.
    But what about static var’s that hold a simple data type? Is there a potential pitfall? Thanks.

    • Luke Daffron Says:

      Yes, there would be some potential for issues.

      Since ASP.NET requests are basically threads in a single app, you have to worry about thread safety for any static variable you might use. Weird things can happen if, for instance, one user hits a page on your site that modifies the static variable while another user hits a page that attempts to read it at exactly the same time.

      Here’s the wikipedia entry on thread safety for more info: http://en.wikipedia.org/wiki/Thread-safety

      Because simple types are much smaller, and thus reads/writes are much faster, it is going to much more rare that you experience the issues – but the potential certainly exists.

  2. RT Says:

    Newbie to the OO world. This issue has been bugging me for sometime now.

    Would you then inject the data context to the DAL/repository class? Now, your BAL is strongly tied to your DAL, isn’t it? Maybe, the example is a bad choice to illustrate the point in question?

    Thanks.

    • Luke Daffron Says:

      Somewhat, yes. But the rubber has to meet the road somewhere – the BAL has to be connected to real data.

      It would certainly be possible to abstract the DataContext further, and weaken that tie. In my case, I think that would do much more harm than good – I don’t want lots of boiler plate ObjectCollection classes (or similar) that I’ll have to maintain.

  3. Surinder Longia Says:

    Hey Luke,
    We are designing a asp.net mvc 2 application as layered architecture. what we created till now is as
    UI Layer
    Service Layer(WCF)
    Business Layer
    Data Layer(Using Unity from Enterprise Library)
    We created all DataLayer and Business Layer methods static. Is this wrong?
    As you mentioned above it start creating problem when number of requests increase to web site. My question is, How may requests can be handled without problem using static methods?

    • Luke Daffron Says:

      It varies greatly with the implementations of the methods.

      The problem isn’t really using static methods. The problem is using static variables inside those methods.

      It’s fine to use all static properties and methods as long as all variables are either parameters to the methods or internal variables in the methods.

      However, if you must do something like


      private static int count;
      public static void AddCount()
      {
      count++;
      }

      Then you should know that the ‘count’ variable will live throughout your entire application, and every request to a page will access the exact same instance of ‘count’. So, depending on your hardware and how many/what kinds of static variables you use – you can quickly get memory issues. Or, you can end up with many kinds of Thread Safety (http://en.wikipedia.org/wiki/Thread_safety) related issues (think of each request as a thread of your web application).


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: