Retrofitting Code for the Web

posted on 02/27/08 at 02:45:06 pm by Joel Ross

Yesterday, Scott Reynolds posed a question about a problem he'd run into while making a web interface for his current project. The essence of his issue is that they have a static class that stores the current user and database connection string, and having that be static in a web application is problematic - it gets overwritten whenever someone new logs into the web site. My guess is his class looks something similar to this:

   1:  public static class Data
   2:  {
   3:     private static string _userId;
   4:     public static string UserId
   5:     {
   6:        get { return _userId; }
   7:        set { _userId = value; }
   8:     }
  10:     public static void SetUser(string userId)
  11:     {
  12:        UserId = userId;
  13:     }
  14:  }

He's got 200 classes that use this code, and doesn't want to modify all of those classes, so he asked if anyone had any input.

Well, we do something very similar to this on Tourneytopia, so I piped up, and provided him with a potential solution. I figured I'd share that here so anyone else who may run into this can see it as well.

The problem is not that the class is static. The problem is that the internal storage of the properties is static. Changing it slightly to allow for injection of a storage container will allow it to work with multiple users, while still allowing it to work without modification to the existing code base - the proposition of modifying 200 classes is pretty overwhelming! Here's the modified class:

   1:  public static class Data
   2:  {
   3:     private static string _userId;
   4:     public static string UserId
   5:     {
   6:        get 
   7:        {
   8:           if(_storage == null)
   9:           {
  10:              return _userId; 
  11:           } 
  12:           else 
  13:           {
  14:              return _storage.GetValue("UserId");
  15:           }
  16:        }
  17:        set 
  18:        { 
  19:           if(_storage == null)
  20:           {
  21:              _userId = value; 
  22:           }
  23:           else 
  24:           {
  25:              _storage.SetValue("UserId", value);
  26:           }
  27:        }
  28:     }
  30:     public static void SetUser(string userId)
  31:     {
  32:        UserId = userId;
  33:     }
  35:     private static IStorage _storage = null;
  36:     public static void SetExternalStorage(IStorage storage)
  37:     }
  38:        _storage = storage;
  39:     }
  40:  }

A few things to note about the update. First, it uses an interface (IStorage) to handle storage - as long as it's set. If it's not set, it defaults to the static fields it used previously - meaning the existing clients will continue working as they did in the past. New clients will call SetExternalStorage passing in an IStorage instance when the client loads (i.e., on initialization of the web site). I suppose you could have used an IoC framework to handle it, but that seemed like a bit of overkill to add that just for this.

The interface itself is pretty straight forward, and would reside in the same DLL as the static class above:

   1:  public interface IStorage
   2:  {
   3:     string GetValue(string key);
   4:     void SetValue(string key, string value);
   5:  }

This is a simple example, and you really could (should?) expand the interface to support specific properties that are in the static class - UserId, etc., so your IStorage provides typed access to information. But for simplicity, this will at least demonstrate the solution.

Now, in the web site, you'd implement this interface, using per-user storage as the internal storage mechanism. For this example, I'll use session:

   1:  public class WebStorage : IStorage
   2:  {
   3:     public string GetValue(string key)
   4:     {
   5:        return HttpContext.Current.Session[key].ToString();
   6:     }
   8:     public void SetValue(string key, string value)
   9:     {
  10:        HttpContext.Current.Session[key] = value;
  11:     }
  12:  }

Now the static class uses this class to store items, which store them per user - the ultimate goal.

The last thing you'd have to do is give the static class it's storage implementation on application initialization:

   1:  Data.SetExternalStorage(new WebStorage());

My initial version (for simplicity) used HttpContext in the static Data class directly, but requiring you to have a reference to System.Web in your business layer isn't a good idea, so I reworked it into what you see above.

In Tourneytopia, we use this same idea, but with a singleton, and instead of session, our storage lasts for the duration of the request, so we're really wrapping HttpContext.Current.Items.

Anyway, I'm not sure this is the best solution, or ultimately what Scott used, but it does seem to get the job done.

Categories: ASP.NET, Development, C#