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
Posted
Thu, Feb 3 2005 8:48 AM
by
Brendan Tompkins