Designing and Developing Web Applications Using Microsoft .NET Framework 4: Designing the Application Architecture

  • 10/17/2011

Objective 1.5: Design State Management

As a user visits a website, he might interact with multiple controls on a page, submit a single page multiple times, and view multiple, different pages in a single site. Often, the user will return later to the same site.

Throughout these interactions, your application needs to keep track of state. You need to know any values the user types or selects in a form. Often, you need to track information about a user as he visits multiple pages or makes multiple visits to your site at different times.

Keeping track of this information in a web application is a challenge, however, because HTTP communications are inherently stateless, a web application might run on multiple web servers, and a single user might switch between different networks or clients. Fortunately, ASP.NET provides several state management techniques that can meet almost any need.

This objective includes an overview of those different state management techniques, along with a description of the advantages of each. Most of this objective reviews topics covered by the 70-515 exam. However, the 70-515 exam focused on the details of implementing state management technologies, while the 70-519 exam focuses on choosing the appropriate state management technology for a given scenario. Therefore, the content in this objective is very high level.

Using Application State

ASP.NET provides the Application object, which you can use to store information that can be accessed by any page or user session. The Application object is a dictionary that is useful for storing small amounts of frequently accessed data that is the same for all users.

However, the Application object was primarily intended for backward compatibility with ASP applications. A better alternative is to store application state in global static objects. Global static objects perform better than the Application object and provide strong typing.

Because application state is stored in memory, accessing it is much faster than querying a database. Consider these factors when using application state:

  • Application state is lost when a server is restarted. Therefore, you must initialize data in the Application_Start method in the Global.asax file.

  • Application state is not shared between servers in a web farm. If you need to share data between servers, use session state, or store it in a shared database.

  • Application state is stored in memory. If you store large amounts of data in application state, it reduces the amount of memory available for other applications and caching. Use the Cache object to allow the .NET Framework to automatically remove objects you no longer need from memory.

  • Application state is free-threaded. Because multiple threads can access application state at the same time, you must write thread-safe code that locks and unlocks data so that it is written to by only one thread at a time. Writing thread-safe code increases development time.

Using the Cache Object

Just like the Application object, the Cache object is a dictionary that is available to all pages and sessions in your application. However, while Application stores objects until the application restarts, Cache stores them only until they expire or ASP.NET determines that it needs to free up memory.

When you add an object to the Cache, you have the option of providing an expiration policy. For example, the following code configures ASP.NET to remove the object after one minute:

Sample of Visual Basic.NET Code

Cache.Insert("MyItem", "MyValue", Nothing, DateTime.Now.AddMinutes(1.0), TimeSpan.Zero)

Sample of C# Code

Cache.Insert("MyItem", "MyValue", null, DateTime.Now.AddMinutes(1d),
    System.Web.Caching.Cache.NoSlidingExpiration);

Alternatively, you can create sliding expiration policies and policies that cause a cached object to expire when a file or directory is updated.

You should use the Cache object any time you might need to access the same data again and that data is relatively difficult to retrieve or create. When choosing between storing data in the Cache and Application objects, choose the Cache object in the following circumstances:

  • The original data source might change.

  • You might run low on memory.

  • You will not need the value for the entire lifespan of the application.

Evaluating User State Technologies

User state is any information the server maintains between multiple user requests. ASP.NET provides several ways to store user state:

  • Cookies. Short strings provided by the server that the client includes with each subsequent request. You can store any string on the client and instruct the client to store it for a few seconds or many years, providing a simple and persistent storage technique. Because cookies do not store data on the server, they do not affect server scalability. However, cookies provide no inherent security; they can be easily intercepted or modified. Additionally, because the client must send cookies with every request, large cookies can increase bandwidth usage and slow page response times.

  • Query strings. You can add query strings to hyperlinks on your pages to pass data to other pages. For example, if you link to a page that lists a store’s inventory, you might specify the link like this to show only audiobooks under $10: http://www.contoso.com/products.aspx?category=audiobook&maxprice=10. Within the Products.aspx code-behind file, you could check for the values Request.QueryString[“category”] and Request.QueryString[“maxprice”]. Unlike other forms of application state, query strings are typically maintained if a user shares a link. Relying heavily on query strings can make it more difficult for search engines to index your site.

  • Hidden fields. You can store information within a form by using HTML hidden fields. If the user submits the form, the browser will submit the contents of the hidden field along with any other fields in the form. However, the hidden field won’t be visible to the user. To add a hidden field, simply add an <input> element to the form and set the type to hidden, as this example shows:

    <input type="hidden" name="code" value="93">
  • Session state. Clients identify themselves with a unique session ID, usually using a cookie. The server then retrieves a collection of session data using the user’s unique session ID. Sessions expire after 20 minutes by default and will not be available if the user changes devices. Therefore, session state is useful only for short-term storage.

  • View state. ASP.NET stores view state in encrypted hidden fields in a web form. View state allows ASP.NET to track control values across multiple requests even if session state is disabled. View state can significantly increase page size in a way that cannot be compressed as efficiently as standard HTML. Therefore, you should disable view state when you do not need it.

  • Control state. Control state functions exactly like view state, except it is defined as part of a user control. If a developer disables view state for a page but a user control uses control state, that control state will remain intact.

  • Authentication. If you authenticate users, you can store any information about the user in a database and associate it with their user ID. This allows state to be persistent between visits, even if the user switches devices. However, users must log on to retrieve the state.

You should use cookies to store information about user preferences. Use view state and control state to store data between requests to the same web form on an intranet. Any data requiring security, such as a user’s address, should be stored on a database and accessed only on the server after the user authenticates.

Table 1-4 compares the features of the different application-state storage techniques.

Table 1-4. Application-State Storage Techniques

Cookies

Query strings

Hidden fields

Session state

View state/Control state

Authentication

Uses server resources

X

X

X

Can be shared in a link

X

Can increase bandwidth significantly

X

X

X

Provides some security

X

X

X

Works across multiple views of the same page

X

X

X

X

X

X

Works across different pages in a single visit

X

X

X

X

X

Works across multiple visits from the same device

X

X

Works across multiple visits from different devices

X

Using Session State

Session state involves both a client and server component:

  • The client must identify itself to the server by using a unique session ID.

  • The server must use the session ID to look up that client’s unique Session collection.

The sections that follow discuss the decisions you can make about both the client side and the server side of session state.

Tracking Session on the Client

You have different options for both the client and server components. For the client, you can choose between using cookies or cookieless session state. Using cookies is the default setting, and it is the right choice for the vast majority of websites. Although every modern browser supports cookies, you might be faced with a scenario that requires you to support sessions without cookies.

Cookieless session state appends the unique session ID to the webpage URL. For example, if a client’s session ID is ow2j32ieo233kj4i2ogfj9320, the URL might be http://www.contoso.com/(S(ow2j32ieo233kj4i2ogfj9320))/default.aspx. The altered URL creates several problems:

  • Search engines might receive a different session ID on different visits, causing the search engine to identify different paths to seemingly duplicate content.

  • If a user bookmarks a page and visits it after the session has expired (20 minutes by default), the session ID will no longer be valid.

  • The URL is much longer than normal, making it more difficult for users to share (especially using short message formats such as Twitter).

  • If a user shares a URL with a session ID embedded, the other user can access information stored within her session. This creates a potential security risk.

Because of these concerns, you should choose cookie-based sessions (the default) unless clients cannot use cookies and you must rely on session state.

Storing Session on the Server

Your web server can store session state in one of three ways:

  • InProc. The default, this setting stores session state in the server’s memory. Session state is not shared between different web servers, and it is lost if the web server is restarted.

  • State Server. This setting stores session state on a separate server. Multiple web servers can share state server session state, allowing clients to transparently switch between different web servers in a server farm. Session state is lost if the state server is restarted.

  • SQL Server. This setting stores session state on a computer running SQL Server. Like a state server, multiple web servers can share state server session state. Additionally, session state is maintained if the state server is restarted. If session state must be maintained during a server outage, you can store state in a SQL Server cluster.

InProc session state is sufficient for most applications that use a single web server. If you use multiple web servers (for example, as part of a network load-balancing cluster) and requests from a single client might be sent to a different server, you need to store state on a state server or SQL Server. In this scenario, you must define the same <machineKey> setting (located within <configuration><system.web> in the Web.config file) on every web server.

Because a machine key is automatically generated by default, you need to manually generate a machine key and set the value on every web server. You can use this online tool to quickly generate a <machineKey> value: http://aspnetresources.com/tools/machineKey. The setting in the Web.config will resemble the following:

<configuration>
    <system.web>
        <machineKey
validationKey="8E8992656E0CD2811EA23ADA31DD7F75F199EE9476947E0860FFC9C767992AEDE0B5CFDABA73D059E67AB166491E1342E4101B814135CFE40BC51D55E4F6B4DE"
decryptionKey="8D02DDC2E9CE3647E5F2649DF5BA0F7A30CFAE2D5AE436AE809CA11D3A3F2121"
validation="SHA1" decryption="AES" />
    </system.web>
</configuration>

Creating Custom Page State Persisters

View state has a significant disadvantage: it can dramatically increase page size. This page size increase is especially dramatic when you are using complex controls such as DataGrid. At times, view state size can approach 1 MB, which takes about 15 seconds to transfer across a 512 Kbps link. You can offset this page size increase by disabling view state for controls that don’t need to be persisted between requests, but often that is not sufficient.

View state is the default mechanism for persisting page state, and usually it is the best choice. However, it is not the only choice. View state uses the class System.Web.UI.HIddenFieldPageStatePersister, which derives from the PageStatePersister class. If view state does not meet your needs, you can use the other built-in page state mechanism, SessionPageStatePersister, or derive your own custom class from PageStatePersister.

SessionPageStatePersister stores view state data along with session state data. This eliminates the extra bandwidth consumed by sending view state data back and forth between the client and the server. However, it consumes more server resources by increasing session state size. Additionally, it can fail if you make use of iframes, pop-ups, or AJAX, or if users have multiple pages open simultaneously.

To use a different page state persister, register a custom PageAdapter class. Within the class, override the GetStatePersister method and return an instance of your PageStatePersister. Then use the custom PageAdapter class instead of the default page adapter. This example demonstrates how to use SessionPageStatePersister:

Sample of Visual Basic.NET Code

Public Class SessionPageAdapter
   Inherits System.Web.UI.Adapters.PageAdapter

   Public Overrides Function GetStatePersister() As PageStatePersister
      Return New SessionPageStatePersister(Page)
   End Function 'GetStatePersister

End Class 'SessionPageAdapter

Sample of C# Code

public class SessionPageAdapter : System.Web.UI.Adapters.PageAdapter {
    public override PageStatePersister GetStatePersister() {
        return new SessionPageStatePersister(Page);
    }
}

Objective Summary

  • The Application object provides a dictionary that can be accessed by any page and session. Use it sparingly because any object you add remains in memory until the application restarts.

  • The Cache object provides a dictionary that can be accessed by any page and session, just like the Application object. However, the Cache object allows you to link an item to a dependency so that the item will be automatically removed from the cache.

  • The .NET Framework provides many ways to track user state, including cookies, sessions, and view state. Cookies tend to be reliable, but the browser has to send any information you store in a cookie with every request. Sessions are less reliable and can be used only for temporary information. ASP.NET uses view state to track server control properties between page requests.

  • By default, ASP.NET stores session state information in memory. If you deploy your application to multiple web servers, you should store session state information in a state server or a SQL Server database. By default, ASP.NET uses cookies to identify clients with a specific session. If clients do not support cookies, you can choose cookieless sessions instead. However, cookieless sessions add the session ID to the page URL, which can cause a variety of issues.

  • View state can significantly increase page size. You can reduce view state page size requirements by using a custom page state persister. ASP.NET provides a built-in page state persister that uses session state; however, it only works reliably if users access only one page at a time.

Objective Review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

  1. You are designing a web application that will be deployed across ten servers in a web farm. The servers will be load-balanced using NLBS. Sessions must be maintained if a user is directed to a different server mid-session. Which session type should you use? (Choose all that apply. Each answer forms a complete solution.)

    1. InProc session state

    2. State server session state

    3. SQL Server session state

    4. Cookieless session state

  2. You recently deployed an ASP.NET web application. Although the application performs well on the intranet, mobile users complain that pages take too long to load. You investigate the problem and determine that the size of the view state is very large, increasing page size. How can you solve the problem? (Choose all that apply. Each answer forms part of the complete solution.)

    1. Create a custom PageAdapter class that uses SessionStatePersister.

    2. Create a custom PageAdapter class that uses control state.

    3. Disable view state.

    4. Use dynamic data controls.

  3. Recently, systems administrators scaled a web application you designed from one web server to three web servers. Requests are distributed between the servers using round-robin DNS. Since the upgrade, the administrators have noticed that sessions are reset when a user request is sent to a different server. How can you solve the problem? (Choose all that apply. Each answer forms part of the complete solution.)

    1. Use the StateServer session state mode.

    2. Use cookieless session state.

    3. Create a custom PageStatePersister.

    4. Configure all web servers with the same machine key.