Introduction

This article focuses on the Provider Model design pattern and describes how it can be used to solve certain problems. To help readers to better understand or appreciate this design pattern, I've chosen view state management as an example to demonstrate the usefulness and practicality of this design pattern. So, in this article, we will first understand what the inherent problems of view state are, I will then explain why Provider Model design pattern is important and how we can leverage it to solve the aforementioned problems.

Background

View State - a love-hate relationship with many ASP.NET developers. Developers love it because it does not require any server state resources and is simple to implement. Page and control state retains automatically across successive posts of the same page instance, which makes the magic of the Web Forms model possible. Furthermore, values in view state are hashed, compressed, and encoded for Unicode implementations, thus representing a higher state of security than hidden fields have. On the other hand, a couple of hot issues surround its use, primarily related to security and performance. For security, because view state is stored in a hidden field on the page. Although view state stores data in a hashed format, the information stored in the hidden field can be seen if the page output source is viewed directly, creating a potential security issue. For performance, because view state is stored in the page itself, storing large values can cause the page to slow down when browsers display or post it.

For those new to ASP.NET, there are a number of good resources for learning more about ASP.NET view state.

Fortunately, the Page class can support alternative storage schemes for view state. The class contains a couple of protected virtual methods that the runtime uses to deserialize or serialize the view state. LoadPageStateFromPersistenceMedium is used to restore the view state at the beginning of the page lifecycle. By contrast, a method named SavePageStateToPersistenceMedium is used to persist the view state just before the page is rendered:

By overriding both methods, you can manipulate the view state at your will. (Note that you cannot override only one method; you have to override both.) The typical solutions are:

Compress the view state (Performance)

Encrypt the view state (Security)

Save view state in a server side file or database table and read/write the view state whenever that page is being processed. (Security and Performance)

Every proposed solution above has its pros and cons. As we all know, there is always a trade-off to choose between security and performance. The problem is how we can design our application in such a way that it can implement these solutions but selectively choose the best for a particular problem domain. For example, you might want to store the view state in a Microsoft SQL Server database for your development tasks and a different one (say Oracle database, which is requested by customer) in production. Choosing the right solution is therefore a matter of matching your needs with respect to how it can solve the problem effectively. Wouldn't it be great if you could implement these solutions on top of a repository that best suits your needs, while at the same time keep the application's architecture, design, and code independent of this choice?

Introducing the Provider Model Design Pattern

Provider Model design pattern derives from a number of widely accepted patterns, and was officially named in the summer of 2002. A "provider" is defined as a pluggable component that extends, or fully replaces, existing system functionality. In other words, the provider model allows you to unplug the default implementation of an API (for example, view state management, session state management, personalization, and so on) and plug your own in. By writing your own provider for a specific system feature that supports the model, you can change the underlying implementation, data formats, and storage medium, without disrupting the application design (keeping the top-level interface intact). More simply put: it allows developers to publish well-documented, easy to understand APIs (Object Model), but at the same time give developers complete control over the internals of what occurs when those APIs are called.

Features implementation that have providers must have a configuration section defined in the web.config (Web applications) or app.config (Windows applications) file. Its purpose is to register the available "providers" but choose one as the default provider. For example:

Based on the configuration shown above, the following outlines a few important guidelines to be followed:

defaultProvider

Each feature configuration should specify the default provider, which instructs the system which of the listed providers to use. For example:

<viewstatedefaultProvider="SqlViewStateProvider">

Provider-friendly name

When defining a provider within configuration, it is required for the name attribute to be defined. Furthermore, the provider names should follow a pattern to easily distinguish who owns the providers. The suggested pattern is: [Provider Creator][Data Store][Feature]Provider.

Table below calls out some of the common names and casing that should be used for various data stores (where name is [Name][Feature]Provider]. For example, SqlViewStateProvider should be used to name the provider which uses SQL Server as the data store.

NAME

Applies to

Sql

Any provider that uses SQL Server as the data store.

Access

Any provider that uses an Access/Jet database as the data store.

Xml

Any provider that uses an XML file as the data store.

AD

Any provider that uses Active Directory as the data store.

File

Any provider that uses a file as the data store.

Memory

Any provider that uses an in-memory data store.

Provider type

When defining a provider within configuration, it is required for the type attribute to be defined. The type value must be a fully qualified name following the format:

ViewState Provider Object Model

The model defines a set of classes to support the view state provider framework. The diagram shown below depicts the ViewState Provider Object Model and its interaction with Page.

ViewStateManager - It exposes two static methods, LoadPageState and SaveViewState, which are used by the application (Page, in our context) to load or save its view state, respectively. It contains no business logic; instead it simply forwards these calls to the configured provider, say SqlViewStateProvider. It is the responsibility of the SqlViewStateProvider provider class to contain the implementation of these methods, calling whatever Business Logic Layer (BLL) or Data Access Layer (DAL) to complete the job.

///<summary>/// Loads any saved view-state of the current page from virtually any
/// storage medium other than a hidden field
///</summary>///<paramname="pControl">System.Web.UI.Page</param>///<returns>The saved view state</returns>publicstatic System.ObjectLoadPageState(System.Web.UI.Control pControl)
///<summary>/// Saves any view-state information of the page to virtually any
/// storage medium other than a hidden field
///</summary>///<paramname="pControl">System.Web.UI.Page</param>///<paramname="viewState">An System.Object in which
/// to store the view-stateinformation</param>publicstaticvoidSavePageState (System.Web.UI.Control pControl,
System.Object viewState)

ProviderBase - is used to mark implementers as a provider, and forces the implementation of a required method and property common to all providers.

ViewStateProviderBase - it exposes a standard interface (well-known APIs) to the view state management service, and all the custom ViewState providers must inherit from this class. The well-known APIs are:

ViewStateConfigurationHandler - it interprets and processes the settings defined in XML tags within the <viewstate> section in the Web.config file and returns an appropriate configuration object based on the configuration settings.

ViewStateConfiguration - it contains the settings information for all the ViewState providers defined in the Web.config file.

SqlViewStateProvider - The implementation

SqlViewStateProvider provider inherits from ViewStateProviderBase class, it uses SQL Server as the data store to store and retrieve the view state of pages during the page life cycle. The table schema designed for storing the view state is as simple as the diagram shown below:

Field Name

Data Type

Description

vsKey

NVARCHAR(100)

An unique key to identify the view state of a particular page

vsValue

NTEXT

The view state of a page.

TimeStamp

DATETIME

The last visit timestamp of this view state

Before a page renders its output, the SavePageState method will be called by the framework to save the page's view state. Internally, it checks if a hidden field (a.k.a. HtmlInputHidden control) named "__vsKey" exists in the page's Controls collection, the hidden field will be created if it doesn't exist. The method's code simply creates an instance of the LosFormatter object and invokes its Serialize() method, serializing the passed-in view state information to the StringWriter writer. Following that, a globally unique identifier (GUID) will be generated and used as the key to save the view state of a particular page to a database table. Last, but not least, this GUID is stored in the hidden field of this page.

When the page posts back, LoadPageState method will be called by the framework to retrieve the saved view state of a particular page. This is accomplished by using the GUID stored in the "__vsKey" hidden field on the last visit to lookup the view state in the database table, and returning the deserialized object via the Derialize() method of LosFormatter.

There is one problem here, each time a user visits a different page, a new record holding that page's view state will be created in the database table. Over time, this will lead to millions of records, which may seriously affect the performance of the lookup process. Some sort of automated task would be needed to periodically clean out the view state records older than a certain date. I leave this as an exercise for the reader.

The diagrams below show the differences before and after using the SqlViewStateProvider provider to store the view state in database instead of storing them in a hidden field named "__VIEWSTATE " of the page.

View State stored in hidden field (Before)

View State stored in database (After)

Using the code

This article ships with two view state providers, available from the link at the top of this article. One of the providers store view state into a SQL Server database table, which is described throughout this article, and the other has the capability of compressing the view state. As noted earlier, if the default functionality of these providers do not meet your needs, you can create your own provider and plug it into the framework.

With ViewState Provider Framework in place, developing a custom view state provider is as easy as the few steps listed below:

Create a new class and derive it from ViewStateProviderBase class. (Remember to follow the naming patterns for provider classes).

Implement the LoadPageState and SavePageState APIs.

Add the new class to the <providers> section in the Web.config file.

Set the defaultProvider attribute's value of <viewstate> section in the web.config file to the newly created provider name.

Conclusion

The provider design pattern allows developers to have a flexible design and a rich, enterprise-level extensibility model. More importantly, you can very easily use one provider for your development tasks, and a different one for the application in production, simply by changing some configuration. Last, but not least, this is an important new design pattern in ASP.NET 2.0, which is extensively used in many common web application features like site membership, role management, personalization, etc. So, start using it in your applications today, and and you'll be ahead of the curve in understanding this design pattern in ASP.NET 2.0 tomorrow.

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.

Share

About the Author

Elvin Cheng is currently living in Woodlands, Singapore. He has been developing applications with the .NET Framework, using C# and ASP.NET since October 2002. Elvin specializes in building Real-time monitoring and tracking information system for Semi-conductor manufacturing industry. During his spare time, he enjoys reading books, watching movie and gym.

Benchmarks would be good, but I'd be surprised if this would add a significant performance hit. I wouldn't be surprised if it made things run a bit faster.

ViewState is inherently a large binary object. Converting it to base 64 text takes time and adds 33% to the size of the object. ASP.NET then has to write this base 64 string out to the client and read it back in. Farming the ViewState out to memory should be faster than doing all this -- farming it out to a SQL Server might be the same or slower, but probably necessary if you have a lot of users.

Pluggable providers are great, but I was hoping to find something on the automatic collection of the ViewState. Like you said, everytime a user goes to a new page, a new ViewState is created, meaning a new chunk of memory. (Postback pages reuse their existing chunk.) The old chunks need to be cleaned up after a suitable timeout period. I'm using the cache collector pattern (Nock) at the moment to do this, but I was hoping to see what others are using.

Before implementing anything like this, you need to do your math. How many new pages do you serve up in a 20 minute period? Multiply that by your average ViewState size. Thats the amount of RAM (or disk space, or database space) you need.

Just a comment on the article. Why use another hidden field to hold your GUID? You've already got one -- the ViewState hidden field! See below...