Let me begin by saying that despite ASP.NET's strengths, its state management leaves some things to be desired. If you've read some of my other blog posts you might not be surprised to hear me say that... I'm known in my office for being an old codger (at 37) who complains about lots of things.
The first issue is that the state management mechanisms in ASP.NET mix the concepts of scope and lifetime. It's true that there is a loose correlation between the two; for example, Application state is scoped to all users of a given application, and lasts for the duration of that application. Session state is accessible to the session, and lasts for the duration of the session. The third option, ViewState, is scoped roughly to the session (actually more narrowly), and lasts across multiple requests for a single form. You could say it's scoped to the form, but I think it's useful to define it based on lifetime.
Now examine the statements above. The phrases "application state" and "session state" are pretty intuitive... but "view state"? That should have been named "form state", except for the inconvenience of wanting it for individual controls as well as the form. The distinction between lifetime and scope is never made, perhaps because with only these three options, the distinction is somewhat moot.
But once you're aware of the distinction, other possibilities present themselves. For example, why can't we have state that persists for the duration of a "business process" (across multiple forms), instead of for the lifetime of a session? What about state scoped to multiple users - all administrators, for example? Or state with a lifetime spanning multiple sessions? All of these would be useful for certain scenarios.
For anything but the three supported options, you're on your own. Web developers have gotten good at using all the various techniques at their disposal - for instance using cookies to preserve state across sessions, using page-level variables to maintain state during the processing of a single page, etc. But basically, once you need more than what the three main state facilities give you, you're on your own.
So there's a learning curve issue here in that state is not accessed through a consistent mechanism - new developers must learn not only how to use Application, Session, and ViewState, but cookies, Cache, page variables, hidden HTML fields, HTTP context, thread-local storage, etc. etc.
The problem is worsened by the fact that each of these mechanisms has implications relating to the physical implementation, so the developer has to worry about those too. For example, using the logical concept of "state scoped to the form with the lifetime of the form" means using ViewState, which means sending data to the client and back on each request. With the exception of Session, ASP.NET doesn't really give you any options when it comes to the physical implementation.
As if all that weren't enough, in a multi-tiered application, maintaining state should not be be the presentation layer's concern. When the presentation layer maintains state, one of two things will generally happen: either the presentation layer will need to pass state to the business layer, or the business layer will need to know more than it should about the presentation layer. Both are bad.
Finally, none of the state mechanisms in ASP.NET really offer immutability as a way to address multi-threading issues. This might or might not be considered a fault of the platform, but if it existed and were transparent it would be handy.
In summary, I think there are a number of problems with ASP.NET state management:
-
confusion of scope and lifetime;
-
limited state options;
-
management of state through many independent, inconsistent mechanisms;
-
dependence on the physical implementation of each mechanism;
-
blurring of lines between the presentation layer and business layer;
-
lack of immutability.
What can we do about it? I propose a state manager that attempts to do the following:
-
manage state based on various combinations of scope and lifetime, providing options beyond what ASP.NET and related facilities offer;
-
expose state capabilities through a single consistent interface;
-
hide the underlying physical implementation of each type of state, and allow use of different implementations where appropriate;
-
explicitly support the business layer, and maintain a clean separation between the business layer and presentation layer.
As I envision it, this state manager would be a class in the business layer, with a limited set of interfaces for supporting "state providers" that would manage the actual state data. The class would enforce serializability of state data; although this is not required for the underlying implementations of all state providers, requiring it up front preserves the ability to switch implementations. All access to state data would occur through the class, probably through static methods. The specification of supported state options, and the mapping of providers to those options, would be done through configuration.
The state manager would (ideally) support notions of scope based on the following:
-
Application: accessible by all users of an application;
-
Role: specific to all users with a certain role;
-
User: specific to an individual user (note that this is NOT the same as session);
-
Session*: specific to all requests in a given session. This has lifetime implications because the session is only valid for a certain length of time;
-
Business Process*: specific to requests associated with a given logical business process. This also may have lifetime implications due to the finite length of the business process, but at any rate it's separate from session scope. Consider multiple sessions associated with the same business process, for example;
-
Form*: specific to the processing of a given form, from initial entry (IsPostBack=false) to the user leaving the form. Equivalent to ViewState;
-
Request*: specific to the processing of a given request.
*Has lifetime implications.
The state manager would support similar options with regard to lifetime:
-
Application: lasts the duration of the application;
-
Session: lasts for the duration of the current session;
-
Business Process: lasts for the duration of a logical business process, which may or may not span multiple sessions;
-
Form: lasts for the duration of processing of a given form, from initial entry (IsPostBack=false) to the user leaving the form. Equivalent to ViewState;
-
Request: lasts for the duration of processing of a given request.
It might be useful to put the scope vs. lifetime options above on a grid. Some combinations might be unworkable, but I think a surprising number of them would be useful.
To limit the knowledge the business tier would have of the presentation tier, an inversion-of-control pattern could be used. Interfaces could expose state capabilities available in the presentation layer, without requiring a direct reference to the System.Web assembly. The presentation layer would pass a reference to a class (or classes) in that layer that implement the appropriate interfaces, allowing the state manager to use it to take advantage of things like ViewState.
A key goal here is divorce the logical concepts of state from their physical implementations. The underlying implementations will always impose constraints - for example, storing session state in the database will make it difficult to know when the session ends. But when enough providers are implemented options will become available. In time, for example, I might be able to reconfigure the app to store form state (ViewState) on the server rather than sending it to the browser, without having to touch a line of code.
Sure would be nice :-)