OBSOLETE CONTENT
The author of
this post has determined that this content is obsolete. Use at your own
risk! Blog posts are a point-in-time snapshot of the blogger's thinking
and should not be assumed to represent this blogger's current opinions.
This post was left up for historical purposes.
State management in web applications can be tricky. ASP.NET
and the .NET framework offers some help in this regard, with controls
that can maintain their own state. Often, however, you
may find yourself storing things in Session or ViewState to
maintain other stateful properties of your applications.
You've really got two options for doing this 1) Store objects
individually in view state, or 2) Create a state class object
that exposes your state objects as typed public properties.
There's really no question here as to which is the better option.
With #1 you have to manage hash keys, and box and unbox your
objects. You also have no way of simply re-setting state, or
restoring state from persistent storage, such as a database. Mark DiGiovanni has blogged about the advantages of maintaining state in one place.
So, how do you go about this? I've developed a method of doing
this that I'm pretty happy with. It involves the use of a base
State class (StateClassBase) that is meant to be sub-classed when you
wish to maintain state. This class will handle storing state in
Session or ViewState, depending on your implementation.
Here's the class. (I've removed comments for simplicity)
[Serializable()]
public class StateClassBase
{
private string _stateKey;
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.SessionState.HttpSessionState 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 StateRemove(System.Web.UI.StateBag ViewState) {
ViewState[_stateKey] = null;
}
public virtual void StateRemove(System.Web.SessionState.HttpSessionState ViewState) {
ViewState[_stateKey] = null;
}
}
Pretty simple, huh? I've found that it's best for each control
that needs to store state to store its own state using a derived state
class object. This way, I can reset state for one part of the
application without affecting another. If I need the state to
persist only for the lifetime of the page, I pass the current StateBag
object (ViewState) to the StateClassBase methods. If I need
more persistent state, or if I'm storing a lot of data, I pass
a HttpSessionState object (Session).
The following design pattern seems to work best for my
purposes. The example is for maintaining page index and sort
expressions for a DataGrid.
- Create a derived StateClass object
public class DataGridPageState : VIT.Common.Classes.StateClassBase
{
private int m_intCurrentPageIndex = 0;
private string m_strCurrentSortExpression;
public DataGridPlusPageState(string uniqueID) : base(uniqueID + "DATA_GRID_PAGE_STATE"){}
public int CurrentPageIndex
{
get {return m_intCurrentPageIndex; }
set {m_intCurrentPageIndex = value; }
}
public string SortExpression
{
get {return m_strCurrentSortExpression; }
set {m_strCurrentSortExpression = value; }
}
}
- Create a private class-level member instance of this object
private DataGridPageState m_pageState;
- In the page or control's OnInit() method, restore this member instance from your state store
this.m_pageState = (DataGridPageState) new DataGridPageState(this.UniqueID).StateRestore(this.Session);
if(this.m_pageState == null) this.m_pageState = new DataGridPageState(this.UniqueID);
- Call the StateStore method after setting any property on the
object. This example is from the event handler for the
DataGrid's PageChanged method.
this.m_pageState.CurrentPageIndex = e.NewPageIndex;
this.m_pageState.StateStore(this.Session);
- When you need access to your state, simply use your member variable, which should always contain the current state, like so:
this.DataGridPlus1.PageSize = m_pageState.PageSize;
That's it! You now have your state in a nice package with
typed versions of your state objects to work with. You can use
this pattern from any control that has access to the System.Web
namespace.
-Brendan