CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Brendan Tompkins [MVP]

Blog First. Ask Questions Later.

StateClassBase Redux

I've been having a good discussion on how best to encapsulate your ASP.NET page state over on Raymond's blog.  Raymond has advocated adding a State object to your PageBase class that has slots for each important session object that you're going to use (correct me if I'm wrong Raymond)..

The nice thing about this approach, is that 1) it makes your session info strongly typed, and 2) it is always there, easily accessible. The downside to this, IMO, is that 1) you end up with a bloated state object, with many possibly unused members, depending on the what the end-user is doing and 2) any controls that depend on this information become tightly coupled to your PageBase Class

The solution I've been using for a while works well, but I think it needs some more thinking. I've created a StateClassBase, which I've posted about before, but here's the latest incarnation:

  [Serializable()]

  public class StateClassBase

  {

    private string _stateKey;

 

    public string stateKey

    {

      get

      {

        return _stateKey;

      }

      set

      {

        if (_stateKey == value)

          return;

        _stateKey = value;

      }

    }

 

    public StateClassBase(string stateKey)

    {

      _stateKey = stateKey;

    }

 

    public virtual object StateRestore(System.Web.UI.StateBag ViewState)

    {

      if(ViewState[_stateKey] != null)

        return ViewState[_stateKey];

      else

        return null;

    }

 

    public virtual object StateRestore(System.Web.HttpApplicationState State)

    {

      if(State[_stateKey] != null)

        return State[_stateKey];

      else

        return null;

    }

 

    public virtual object StateRestore(System.Web.SessionState.HttpSessionState ViewState)

    {

      if(ViewState[_stateKey] != null)

        return ViewState[_stateKey];

      else

        return null;

    }

 

    public virtual object StateRestore(System.Web.Caching.Cache ViewState)

    {

      if(ViewState[_stateKey] != null)

        return ViewState[_stateKey];

      else

        return null;

    }

 

    public virtual void StateStore(System.Web.UI.StateBag ViewState)

    {

      ViewState[_stateKey] = this;

    }

 

    public virtual void StateStore(System.Web.SessionState.HttpSessionState ViewState)

    {

      ViewState[_stateKey] = this;

    }

 

    public virtual void StateStore(System.Web.HttpApplicationState State)

    {

      State.Lock();

      State[_stateKey] = this;

      State.UnLock();

    }

 

    public virtual void StateStore(System.Web.Caching.Cache ViewState, System.TimeSpan timeToExpire)

    {

      ViewState.Insert(_stateKey, this, null, System.DateTime.Now + timeToExpire, TimeSpan.Zero);

    }

 

 

    public virtual void StateRemove(System.Web.UI.StateBag ViewState)

    {

      ViewState[_stateKey] = null;

    }

 

    public virtual void StateRemove(System.Web.SessionState.HttpSessionState ViewState)

    {

      ViewState[_stateKey] = null;

    }

 

    public virtual void StateRemove(System.Web.HttpApplicationState State)

    {

      State.Lock();

      State.Remove(_stateKey);

      State.UnLock();

    }

 

    public virtual void StateRemove(System.Web.Caching.Cache ViewState)

    {

      ViewState.Remove(_stateKey);

    }

 

    // Removes all instances in session

    public static void StateRemove(System.Type typ, System.Web.SessionState.HttpSessionState ViewState)

    {

      string [] keys = new string[ViewState.Keys.Count];

      for(int i = 0; i < ViewState.Keys.Count; i++)

      {

        keys[i] = ViewState.Keys[i];

      }

 

      foreach(string key in keys)

      {

        if(ViewState[key] != null && ViewState[key].GetType() == typ)

        {

          ((StateClassBase)ViewState[key]).StateRemove(ViewState);

        }

      }           

    }

  }

To use this in a control (any System.Web.UI.Control including Page) you derive from this base class, and add your strongly typed members.

    ///

    /// Object for maintaining UsersOnlineControl state

    ///

    [Serializable]

    public class UsersOnlineControlState : StateClassBase

    {

      public UsersOnlineControlState() : base("UsersOnlineControl_STATE")

      {}

 

      private bool chatOn = true;

 

      public bool ChatOn

      {

        get { return chatOn; }

        set { chatOn = value; }

      }

 

    }

Then, in your OnInit method, and set a local member of your derived type to the instance.

      // Initialize the State Class

      this.stateclassbase = (UsersOnlineControlState) new UsersOnlineControlState().StateRestore(this.Page.Session);

      if (this.stateclassbase == null) this.stateclassbase = new UsersOnlineControlState();

The cool thing about it is that you can store your state in Session, ViewState, Application, or Cache. The other nice advantage is being able to decouple your controls from your page base class. If you have a control that depends on being on your page base to get the state, then it's tightly coupled with your page. If the control just manages it's own state, like this, it can be placed on any page.

Now, this thing does need some work, which is why I wanted to post this.  For one thing it'd be nice not to have to do the boxing and unboxing, or at least not have to code it every time.  Also, it'd be nice to not have to instance it in your OnInit method.  Possibly a re-design with some lazy loading, and some better OO work could help.

-Brendan



Comments

Scott Galloway said:

Brendan, in Firefox, this post messes up the main feed.
# February 3, 2005 10:22 AM

Brendan Tompkins said:

Ooops.. Yes it did. I fixed this.. Thanks! There's a long story here abou how I switch back and forth between IE and Firefox, and how I'm rapidly becoming an IE hater, and now I have zillions of browser windows open at any given time while I try to get used to tabbed browsing, and how IE still seems to pop open from time to time, and I'm trying out SourceToHtml, and on and on. But I wouldn't want to bore you. :)
# February 3, 2005 10:33 AM

Darrell said:

One thing I would change is to make the StateRestore method always return a non-null object. This would make the following line of code unnecessary in the OnInit, having moved it into the StateRestore:
if (this.stateclassbase == null) this.stateclassbase = new UsersOnlineControlState();

This maintains encapsulation as your page does not need to know about the state restoration mechanism.
# February 3, 2005 11:46 AM

Raymond Lewallen said:

Brendan,

I've been looking over your code and kind of working on a few things with it in my head. Question: are you creating a type that inherits the StateBaseClass for each control or object you want to store state information for? As far as the boxing and unboxing, you can take care of that easily enough, but scaling it for each type you want to deal with wouldn't be much fun, so I can see why you leave it as just object. I like the implementation, and will probably take yours and implement a few ideas, rather than take mine and implement your ideas. Yours is a better starting point for where I/we probably want to go with it. Still working on how we can fit the best of both together. I really would like to examine how to reduce the amount of code required for yours though, but thats probably just because I'm spoiled using my implementation.
# February 4, 2005 7:10 AM

Brendan Tompkins said:

"are you creating a type that inherits the StateBaseClass for each control or object you want to store state information for"

Yes, usually I'm working with a Control, or Page, and need to store state for just that control. I subclass this stateclass, and add members to it.

The problem of how to return the instance without boxing from the StateRestore method is the tricky part. Could do the boxing within the subclass itself, right, but that would be a pain, might as well just do it once in the OnInit of your page.

PS. Base class should be abstract..
# February 4, 2005 7:55 AM

TrackBack said:

# February 14, 2005 5:56 PM

TrackBack said:

# February 14, 2005 5:58 PM

TrackBack said:

# February 16, 2005 11:45 AM

TrackBack said:

# February 23, 2005 12:13 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Brendan Tompkins

Brendan has been programming with .NET since the first public beta and is owner and operator of Port Technology Services, a consultancy company providing .NET application development services to the Maritime industry. In July, 2007, he was awarded the Microsoft MVP award for ASP.NET. He's also a proud co-founder of failed .COM startup Intrinsigo, and has had a hand in the failure of numerous other businesses. He currently runs CodeBetter.Com and Devlicio.us, and lives in Norfolk, Virgina with his wife Tiara and son Ian.

View Brendan's profile on LinkedIn

Check out Devlicio.us!

Our Sponsors

Proudly Partnered With


This Blog

Syndication

News

MVP
Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.