Once you split your application into multiple tiers, you quickly find that the business and data tiers don't (well shouldn't) have direct access to the
HttpContext.Current
object and the
.Application
,
.Request
,
.Items
, and
.Session
collections.
The separation allows you to switch UIs or UI libraries without having to overhaul the engine of the application, and is good for organisation, so it's good practice, but has its hassles.
How do you store ASP.NET application state, such as certain database connection parameters, or a database connection object, if you don't have application to
HttpContext.Current.Application
?
One way is to use the provider pattern and abstract the state management behind a simple state framework that has two parts.
The first part is a library with a base class and a
static
property to hold a derived instance of it.
Here's an example of the base state component:
http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.State/BaseStateProvider.cs
Here's an example of the
static
property which holds a copy of the state component:
http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.State/StateAccess.cs
These are in a library which has no reference to
System.Web
and therefore no way to access
HttpContext.Current
. This state library is at the core of the application and all projects requiring state access should reference it.
Now to actually provide all of those low level components access to the state collections offered by
HttpContext.Current
, we introduce the web specific state component.
The web specific state component inherits
BaseStateProvider
and overrides all of the functions. The web specific state component is defined in the presentation library and passed down to the lower levels to use. This allows them to interact with the
System.Web
components indirectly by overriding the functions.
Here's an example of the web specific state provider:
http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.Web/State/WebStateProvider.cs
Each set of functions corresponds to a specific scope and a different state collection.
During
Application_Start
,
Session_Start
, and
Application_BeginRequest
, the web state provider component is initialized and assigned to
StateAccess.State
(if not already initialized).
Here's an example of the initialization call in
ApplicationContext
, which inherits
HttpApplication
and is the code behind for the
Global.asax file:
http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.Web/ApplicationContext.cs#180
Here's an example of the initializer:
http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.Web/State/StateProviderInitializer.cs#41
The initializer above uses modified provider pattern code taken from the MSDN reference and could easily be simplified to just:
StateAccess.State = new WebStateProvider();
So as a result, the library structure is:
Web
↓
Business
↓
Data
↓
Entities
↓
Configuration
↓
Diagnostics
↓
State
The
State
library is at the center/base. Pretty much every other library will reference the core
State
library, which contains the base class and
static
property to hold it.
The web specific state components are in another library in the web tier either in their own library or within a general web assembly.
Now because of this setup, I can create a
db4o IObjectServer
in the data tier and store it using:
StateAccess.State.SetApplication(serverKey, objectServer);
Such as:
http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.Data.Db4o/Db4oDataStore.cs#75
Then retrieve it again using:
IObjectServer objectServer = (IObjectServer)StateAccess.State.GetApplication(serverKey);
Such as:
http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.Data.Db4o/Db4oDataStore.cs#72
This is despite the data assemblies having no direct reference to the
System.Web
library it's being stored in
HttpContext.Current.Application
. This is a good place to store something if you only want a single instance of it for the life of your application.
So the entire project can be switched to a desktop GUI or another UI just by providing a state component for that.
The general
StateProvider
would likely be sufficient for most cases, but a custom one can be provided if needed:
http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.State/StateProvider.cs
It gets used during testing to remove any dependencies on the System.Web library and HttpContext.Current.
This pattern can be reused for a multitude of situations, with a little imagination. You'll likely need to create code specific to that solution.