Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Web Application State

0.00/5 (No votes)
15 Apr 2008 1  
This article explains how to maintain the web application state.

Introduction

Web Application State

A Web page is recreated every time it is posted back to the server. In traditional Web programming, this means that all the information within the page and information in the controls is lost with each round trip. To overcome this limitation of traditional Web programming, the ASP.NET page framework includes various options to help us preserve changes. One option involves keeping information on the client, directly in the page or in a cookie, and another option involves storing information on the server between round trips. The following objects are designed to hold application state: HttpSessionState, HttpApplicationState, ViewState, HttpCookie, HiddenFiled, and also querystrings and databases can act as state holders. So, we can say that there are mainly two different ways to manage a web application’s state: client-side and server-side.

Client-side State Management

Cookies

A cookie is a small amount of data stored either in a text file on the client's file system or in-memory in the client browser session. Cookies are mainly used for tracking data settings. Let’s take an example: say we want to customize a welcome web page; when the user requests the default web page, the application firsts detects if the user has logged in before; we can retrieve the user information from cookies, in this case.

Hidden Fields

The default way of saving the state of data is to use HTML hidden fields. A hidden field stores text data with the HTML <input type="hidden">. A hidden field will not be visible in the browser, but we can set its properties just like we set properties for other standard controls. When a page is posted to the server, the content of a hidden field is sent in the HTTP Form collection along with the values of other controls. In order for hidden field values to be available during page processing, we must submit the page.

ViewState

The Control.ViewState property provides a way for retaining values between multiple requests for the same page. This is the method that the page uses to preserve page and control property values between round trips. When a page is processed, the current state of the page and the controls is hashed into a string and saved in the page as a hidden field. When the page is posted back to the server, the page parses the view state string at page initialization and restores property information in the page.

Query Strings

Query strings provide a simple but limited way of maintaining some state information. You can easily pass information from one page to another, but most browsers and client devices impose a 255-character limit on the length of the URL. In addition, the query values are exposed to the Internet via the URL, so in some cases, security may be an issue. A URL with query strings may look like this: http://myapplication.com/listing.aspx?group=1&item=1.

Server-side State Management

Application Object

The Application object provides a mechanism for storing data that is accessible to all code running within the Web application. The ideal data to insert into application state variables is data that is shared by multiple sessions and which does not change often. And just because it is visible to the entire application, you need to use the Lock and UnLock methods to avoid having conflict values.

Session Object

The Session object can be used for storing session-specific information that needs to be maintained between server round trips and between requests for pages. The Session object is on a per-client basis, which means different clients generate different Session objects. The ideal data to store in session-state variables is short-lived, sensitive data that is specific to an individual session.

Using Database

Maintaining state using database is a very common method when storing user-specific information where the information store is large. Database storage is particularly useful for maintaining long-term state or state that must be preserved even if the server must be restarted. The database approach is often used along with cookies. For example, when a user access an application, he might need to enter a username/password to log in. You can look up the user in your database and then pass a cookie to the user. The cookie might contain only the ID of the user in your database. You can then use the cookie in the requests that follow to find the user information in the database, as needed.

Problem

As was stated above, web applications are stateless, so you need to implement data holders to provide state for web applications. For example, the following code demonstrates the basic usage of the HttpSessionState.

MyDtoObject dtoObject = new MyDtoObject();  
dtoObject.FirstName = Session["FirstName"] as string;
dtoObject.LastName = Session["LastName"] as string;
dtoObject.Login = Session["Login"] as string;
//and so on

But, what if requirements force me to store MyDtoObject in the database for a while? Then, it forces me store MyDtoObject in HttpApplicationState to share it between user sessions. It makes it very difficult if I have to modify batches of files all the time. What if I need ability to define how to store MyDtoObject, without re-coding?

Solution

I believe that using HttpSessionState, HttpCookie, and HttpApplicationState directly is not convenient, so I designed some kind of wrappers for these objects following the OOP paradigm. For instance, I need to have the ApplicationSettings object that would hold the state in cookies, and also, it is supposed to be a singleton object. All that I need is just to make the ApplicationSettings class derive from SingletonStateObject<ApplicationSettings, CookieStorage<ApplicationSettings>>.

public class ApplicationSettings : SingletonStateObject<ApplicationSettings, 
             CookieStorage<ApplicationSettings>>
{
    private string _hostName;
    private int _counter;


    public int Counter
    {
        get { return _counter; }
        set { _counter = value; }
    }

    public string HostName
    {
        get { return _hostName; }
        set { _hostName = value; }
    }
}
   
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    ApplicationSettings settings = ApplicationSettings.Get();
    Response.Write("ApplicationSettings:");
    Response.Write("<br/>");
    Response.Write("HostName: " + settings.HostName);
    Response.Write("<br/>");
    Response.Write("Counter: " + settings.Counter);
    
}

protected void uxSave_Click(object sender, EventArgs e)
{
    ApplicationSettings settings = ApplicationSettings.Get();
    settings.HostName = uxHostName.Text;
    settings.Counter++;
    settings.Save();
    Response.Redirect("~/SingletonDefault.aspx");
}

The SingletonStateObject and StateObject are base classes for any state object. Both these classes deal with IStorage<T> implementators.

StateObjects.JPG

The IStorage interface defines a contract for saving, retrieving, and removing data from storage.

IStorage.JPG

IStorage Implementators

The StateObject, SingletonStateObject, and IStorage implementators together demonstrate the Bridge pattern which allows to define your own state object in a flexible manner. Depending on your needs, you can use a concrete storage implementator or implement your own.

  • Use CookieStorage if you need to store small amounts of information on the client, and security is not an issue.
  • Use ApplicationStorage if you need to store small amounts of information that is infrequently changed, and information is shared between users.
  • Use SessionStorage if you need to store short-lived information that is specific to an individual session, and security is an issue. Do not store large quantities of information in a session state object. Be aware that a session state object will be created and maintained for the lifetime of every session in your application. In applications hosting many users, this can occupy significant server resources and affect scalability.
  • Use DoaStorage if you need to store large amounts of information managing transactions, or the information must survive application and session restarts, and security is an issue.

DaoStorage Implementation

Let's get back to the ApplicationSettings object. What if requirements force me to change storage and ApplicationSettings is not singleton any more? The requirements force storage to survive Application and Session restarts, so we need to use a database to hold state. There is a Heap table that has Key and Value columns in the MS Access database attached to the sample project. All that I need to do is to serialize the object and save it using the provided key.

public class DaoStorage<T> : IStorage<T> where T : class
{

    public void Save(object key, T obj)
    {
        string value = Serializer<T>.Serialize(obj);

        using (OleDbConnection connection = 
               new OleDbConnection(Config.ConnectionString))
        {
            connection.Open();
            if (IsExist(connection, key))
                Update(connection, key, value);
            else
                Insert(connection, key, value);
        }
    }


    public T Get(object key)
    {
        using (OleDbConnection connection = 
               new OleDbConnection(Config.ConnectionString))
        {
            connection.Open();
            string obj = Get(connection, key);
            return Serializer<T>.Deserialize(obj);
        }
    }

    public void Delete(object key)
    {
        using (OleDbConnection connection = 
               new OleDbConnection(Config.ConnectionString))
        {
            connection.Open();
            Delete(connection, key);
        }
    }


    public List<T> GetAll(Type type)
    {
        using (OleDbConnection connection = 
               new OleDbConnection(Config.ConnectionString))
        {
            connection.Open();
            return GetAll(connection);
        }
    }


    public void Clear()
    {
        using (OleDbConnection connection = 
               new OleDbConnection(Config.ConnectionString))
        {
            connection.Open();
            Clear(connection);
        }
    }
....

In this class, I use XmlSerialazer to put objects into database, so we need to keep in mind that ApplicationSettigs must be XmlSerializable. Let's modify the ApplicationSettings class:

public class ApplicationSettings : StateObject<ApplicationSettings, 
             DaoStorage<ApplicationSettings>>
{
    private string _hostName;
    private int _counter;


    public int Counter
    {
        get { return _counter; }
        set { _counter = value; }
    }

    public string HostName
    {
        get { return _hostName; }
        set { _hostName = value; }
    }
}

That is all, now the ApplicationSettings class is not singleton, and also doesn't depend on Application and Session restarts any more. Instead of using HttpSessionState or HttpApplicationState, it serializes its state into the database.

Scalability

Using HttpSessionState has performance issues and using cookies has security issues. There are many alternative storages to use, but what if I need to have a configurable storage strategy for a group of objects? Let's design the ConfigurableStorage which allows us to define a storage strategy from configuration.

public class ConfigurableStorage<T> : IStorage<T> where T : class
{
    private readonly IStorage<T> _storage = ResolveStorage();

    private static IStorage<T> ResolveStorage()
    {
        switch (Config.StorageType)
        {
            case StorageType.AppicationStorage:
                return new ApplicationStorage<T>();
            case StorageType.SessionStorage:
                return new SessionStorage<T>();
            case StorageType.FileStorage:
                return new FileStorage<T>();
            case StorageType.DaoStorage:
                return new DaoStorage<T>();
            case StorageType.CookieStorage:
                return new CookieStorage<T>();
            default:
                throw new ApplicationException("StorageType");
        }
    }

    public void Save(object key, T obj)
    {
        _storage.Save(key, obj);
    }

    public T Get(object key)
    {
        return _storage.Get(key);
    }
....

As you see, the ConfigurableStorage is a simple class that creates another type of storage and uses it to save object states. It also demonstrates the Proxy pattern. Let's modify the ApplicationSettings class:

public class ApplicationSettings : StateObject<ApplicationSettings, 
             ConfigurableStorage<ApplicationSettings>>
{
    private string _hostName;
    private int _counter;


    public int Counter
    {
        get { return _counter; }
        set { _counter = value; }
    }

    public string HostName
    {
        get { return _hostName; }
        set { _hostName = value; }
    }
}

Now, the ApplicationSettings derives from ConfigurableStorage as well as other classes that are supposed to be configurable. It is the most painless approach to change the storage strategy for a group of objects in an application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here