Creating a better way to manage ASP.NET state has
been talked about quite a bit. I’ve posted about it a
few times, as have others. A couple of weeks ago Raymond brought the topic up again,
and it got me thinking that the way I've been doing it
could probably be improved upon. Today I needed to look at
it again for our public web site, so I thought that it was a good time
for a rewrite.
If you have a look at my StateClassBase Redux there’s
a number of problems with the implementation. For one, it’s a poor use
of OO. Yes, there is a base class, but it just contains a bunch
of overloaded methods. In fact, having a bunch of overloaded
methods is usually a good indication that your design isn’t all that
great. For another, it was cumbersome to use, and required that
it be initialized in your code before you use it. Initializing
the class in your OnInit worked okay sometimes, but could also be buggy
in other situations, like when your control was dynamically
loaded. The new version lazy loads itself, via a singleton-ish
Instance property, which helps sort out these issues.
It also wasn’t easy to re-use. If I wanted to add another back-end
session storage mechanism, it involved modifying the base class
itself. What was I thinking?
Recent Updates (2/24/05):
-
Added [Serializable] to the class and [XmlIgnore] to the Instance
-
Made the SessionStateObject class Abstract
So, here’s my new and improved StateBase class:
[Serializable]public abstract class StateBase
{
protected string stateKey;
private object instance;
/// <summary>
/// Gets the instance.
/// </summary>
/// <value></value>
[XmlIgnore]public virtual Object Instance
{
get
{
// Lazy Load
if(instance == null)
{
// Try to restore
Object o = Restore();
if(o != null)
{
instance = o;
return o;
}
// Create a new instance of the derived type
Type type = this.GetType();
instance = Activator.CreateInstance(type);
}
return instance;
}
}
/// <summary>
/// Gets or sets the state key.
/// </summary>
/// <value></value>
public string StateKey
{
get { return stateKey; }
set { stateKey = value; }
}
/// <summary>
/// Creates a new <see cref="StateBase"/> instance.
/// </summary>
/// <param name="stateKey">State key.</param>
public StateBase(string stateKey)
{
this.stateKey = stateKey;
}
/// <summary>
/// Stores this instance.
/// </summary>
/// <returns></returns>
public abstract void Store();
/// <summary>
/// Removes this instance.
/// </summary>
/// <returns></returns>
public abstract void Remove();
/// <summary>
/// Restores this instance.
/// </summary>
/// <returns></returns>
public abstract Object Restore();
}
And here’s a derived class that sores the state in Session.
Note that you can create one that stores state in ViewState, or
Application or Cache or in the Database, or on your cell phone. The
point is, you just derive from StateBase, and implement the abstract
methods.
public abstract class SessionStateObject : StateBase
{
/// <summary>
/// Creates a new <see cref="SessionStateObject"/> instance.
/// </summary>
/// <param name="stateKey">State key.</param>
public SessionStateObject(string stateKey) : base(stateKey)
{}
/// <summary>
/// Stores this instance.
/// </summary>
/// <returns></returns>
public override void Store()
{
System.Web.HttpContext.Current.Session[stateKey] = this;
}
/// <summary>
/// Removes this instance.
/// </summary>
/// <returns></returns>
public override void Remove()
{
System.Web.HttpContext.Current.Session.Remove(stateKey);
}
/// <summary>
/// Restores this instance.
/// </summary>
/// <returns></returns>
public override Object Restore()
{
return System.Web.HttpContext.Current.Session[stateKey];
}
}
And here’s how to use it in your code, simply
derive from the SessionStateObject, adding any members you want to keep
track of your state.
/// <summary>
/// Object for maintaining state
/// </summary>
public class SomeControlsState : SessionStateObject
{
public SomeControlsState() : base("SOMECONTROL_STATE")
{}
public new SomeControlsState Instance
{
get
{
return (SomeControlsState) base.Instance;
}
}
private ShippingContainerCollection containers;
public ShippingContainerCollection Containers
{
get { return containers; }
set { containers = value; }
}
}
Now, assuming that you’ve created an instance of state (you can
now do this in-line in your class) to save your state,
just call Store();
state.Instance.Containers = someService.Instance.GetShippingContainerCollection();
state.Instance.Store();
Using in on subsequent postbacks is even easier:
ShippingContainer sc = state.Instance.Containers.Find(Convert.ToInt32(e.CommandArgument));
No more worrying about calling Restore() unless you specifically
need to. Hope you find this useful. I really would like
some feedback to this. If this can be done better, I want make
this thing sing!
-Brendan
Posted
Wed, Feb 23 2005 3:07 PM
by
Brendan Tompkins