Are You Sure You Want Server-side Viewstate?

Can You Believe It Raises Scalability Issues?

CoreCoder

LANGUAGES: C#

ASP.NET VERSIONS: 2.0 | 3.5

 

Are You Sure You Want Server-side Viewstate?

Can You Believe It Raises Scalability Issues?

 

By Dino Esposito

 

I recently returned to an evergreen programming topic that always stirs developers interest: server-side viewstate (see Server Viewstate Revisited). In particular, I focused on a couple of aspects. First, the serialization algorithm that silently changed in transition from ASP.NET 1.x to ASP.NET 2.0 mitigates the impact of the problem simply by making the size of the viewstate field significantly smaller. Some 40% of reduced viewstate, on average, is definitely a good result for everybody. Second, for those still willing to keep the viewstate field off their client pages, a new persistence algorithm has been developed mostly due to the internal refactoring of the viewstate management process within the Page class.

 

Keeping the viewstate content on the server, though, is a much less appealing trick than it may appear at first glance. You can t simply limit it to maintain one copy of the viewstate per page. If this sounds like a weird constraint, think of a user who requests the same page several times in the same session. For the sake of consistency, you should create a new file or record for each page request and ensure you employ a proper naming convention to prevent overriding page state. What if the user navigates back to the previously visited page? You must be able to retrieve the viewstate of exactly that instance of the page.

 

With a hidden field in the folds of the client page, the problem is brilliantly solved in a way that doesn t affect the server. If a server-side storage for the viewstate is wanted, then it is up to you to manage the issue of multiple page instances. This means that either you put an upper limit to the number of page instances that can be maintained (thus limiting the browser s history navigation) or you accept a potential scalability risk. The proliferation of server-side viewstate copies eats up memory and server resources. The viewstate is implicit in the Web Forms model; it simply can t be dropped as an unnecessary evil. It is a trade-off, though, and at the end of the day, the client hidden field is the lesser evil. If you still want to keep the viewstate on the server, though, there s another, often overlooked, issue to deal with: What naming convention would you use to track multiple copies of the viewstate per page?

 

Managing Multiple Page Instances

It should be clear by now that you need to maintain a copy of the viewstate per page instance. A viewstate copy per page URL is not enough, and will alter the state of the application. Whether you plan to store viewstate copies on the server s file system, out-of-process memory, or a database, you still have to come up with an effective naming convention. In other words, you need an algorithm to bind the viewstate content to a particular page instance.

 

How would you uniquely identify a page instance? The URL is not enough, and so it is for the session ID. Combining them is a better approach, and this is what we did in last month s article. Unfortunately this trick is wearing thin. In the same session, for the same URL, in fact, you can have multiple page instances. A progressive number appended to the URL and session key can help, but how would the page know about this index? You must be able to build the key based on the request content. Session information and URL are available data, but what about the index? The page itself must be carrying at least this short piece of information.

 

The idea being developed and tested in the remainder of this article entails adding a custom hidden field to the page packed with a GUID (or anyway a unique index or ID). On the server, the viewstate persistence layer will take this GUID and use it to generate the key to retrieve the viewstate copy specific to that page instance.

 

This trick won t alleviate scalability risks related to keeping the viewstate on the server, but at least it gives you an effective algorithm to track all the pieces of it.

 

Revisiting Server-side Viewstate

To help understand why I don t believe much in server-side viewstate, keep in mind what I wrote a moment ago. The following trick will help you track all the pieces of the viewstate. And this is precisely the problem. For consistency, you need to track the viewstate for all page instances; but this soon becomes your next big problem. Let s see how to track viewstate correctly on the server.

 

Last month, I explained in detail what you need to do to store the page viewstate on the server. Figure 1 shows the skeleton of an ASP.NET page that uses server-side viewstate. As you can see, the nerve center of the mechanism is the page state persister class. A custom persister class is built around a couple of Load and Save methods. They are ultimately responsible for loading viewstate from a server-side data store and for saving it where you specify. Figure 2 shows a boilerplate implementation that isolates all the logic of interest in the viewstate persistence layer.

 

public class ServerViewStatePage : Page

{

   private PageStatePersister _persister = null;

   public ServerViewStatePage()

   {

   }

   protected override PageStatePersister PageStatePersister

   {

       get

       {

           if (this._persister == null)

           {

               this._persister = new MyOwnPageStatePersister(this);

           }

           return this._persister;

       }

   }

}

Figure 1: A custom page that saves viewstate on the server.

 

public class MyOwnPageStatePersister : PageStatePersister

{

   public MyOwnPageStatePersister(Page page) : base(page)

   {

   }

   public override void Load()

   {

       string pageState = String.Empty;

        // Get it from store

       pageState = ViewStatePersistenceLayer.Load(base.Page);

       // Attach viewstate to the page

       if (!string.IsNullOrEmpty(pageState))

       {

           Pair pair = (Pair)base.StateFormatter.Deserialize(pageState);

           base.ViewState = pair.First;

           base.ControlState = pair.Second;

       }

   }

   public override void Save()

   {

       string pageState = String.Empty;

       // Get control and view state

       if ((base.ViewState != null) || (base.ControlState != null))

       {

           pageState = base.StateFormatter.Serialize(new Pair(base.ViewState,

              base.ControlState));

       }

       // Save it off somewhere

       ViewStatePersistenceLayer.Save(base.Page, pageState);

       return;

   }

}

Figure 2: A custom persister class using a disk file.

 

The idea is to associate each page instance with a unique value; for example, a GUID. This value is saved to a custom hidden field within the page and takes up only a few bytes. The page saves to a location the viewstate to the server, be it a table record or a disk file, identified by the key value. This way, you can manage on the server any number of instances per each page URL your users visit. Here s a brief excerpt from the Save method of the persistence layer:

 

public static void Save(Page pageInstance, string pageState)

{

   string key = GenerateKey(pageInstance);

   // Save to a disk file

   :

}

 

The full code for the persistence layer is shown in Figure 3.

 

public class ViewStatePersistenceLayer

{

   private const string HIDDENFIELDNAME = "__VSSERVERKEY";

   public static void Save(Page pageInstance, string pageState)

   {

       // Get the key to store the viewstate for THIS page instance

       string key = GenerateKey(pageInstance);

       // Save to a disk file

       string fileName = GetFileName(pageInstance, key);

       StreamWriter writer = new StreamWriter(fileName);

       writer.Write(pageState);

       writer.Close();

   }

    public static string Load(Page pageInstance)

   {

       // Get the key to retrieve the viewstate for THIS page instance

       string key = FindViewStateKey(pageInstance);

       // Get viewstate from disk file

       string file = GetFileName(pageInstance, key);

       StreamReader reader = new StreamReader(file);

       string pageState = reader.ReadToEnd();

       reader.Close();

       return pageState;

   }

   private static string GetFileName(Page pageInstance, string key)

   {

       string pattern = String.Format("ViewStateTemp/{0}.viewstate", key);

       string fileName = pageInstance.Server.MapPath(pattern);

       return fileName;

   }

   private static string FindViewStateKey(Page pageInstance)

   {

       string key = String.Empty;

       object o = pageInstance.Request[HIDDENFIELDNAME];

       if (o != null)

           key = (string)o;

       return key;

   }

   private static string GenerateKey(Page pageInstance)

   {

       string key = Guid.NewGuid().ToString();

       // Register the hidden field

       pageInstance.ClientScript.RegisterHiddenField(HIDDENFIELDNAME, key);

       return key;

   }

}

Figure 3: The viewstate persistence layer.

 

A new key needs to be generated for each request just to preserve visibility on each instance of a given page. As shown in Figure 3, a hidden field is added to the page response from within the GenerateKey helper method:

 

string key = Guid.NewGuid().ToString();

pageInstance.ClientScript.RegisterHiddenField(

 HIDDENFIELDNAME, key);

 

When the ASP.NET runtime gets to process a postback request, it attempts to load the viewstate. It ends up calling into the page state persister component and then in the Load method of our custom viewstate persistence layer. Here, the content of the ad hoc hidden field is retrieved the key value and used to access the viewstate content to use.

 

Can You Spot the Problem?

As the sample code that accompanies this article demonstrates, you can successfully hold the viewstate on the server and still navigate between page instances as you expect. For each request, the amount of data being downloaded and uploaded is significantly reduced. For example, by holding the viewstate on the server in a sample page that contains hundreds of records in a data grid, you reduce page traffic by 50%. Is that all? No dark sides? No drawbacks?

 

Whether you store the server viewstate as records of a database table or files on the disk, you have no easy way to detect when a record has to be removed. On the other hand, if you don t implement an effective policy for periodically removing obsolete copies of the viewstate, you eat up a lot of space, and, subsequently, resources, on the server.

 

Overall, this problem is nothing new. It is simply a variation of the mechanism used by the ASP.NET runtime to manage its temporary files. Periodically, the runtime accesses the internal folders on the server in sweep mode and clears some files those that it reckons safe to delete. As this is an internal aspect of the ASP.NET engine, its details are not relevant here. However, it gives you an idea of what you should do to keep the natural proliferation of records or server files under control. You can run a scheduled job that clears files created a few days or hours before. Alternatively, this job can be done by the same GenerateKey method you use in the viewstate persistence layer.

 

Finally, note that if a page at some point can t find the record or file it is looking for, it won t be able to restore viewstate. Is this a problem? Absolutely; it is the source of some trouble for the user, but it is not necessarily a big deal. Once the user has reloaded the page from the home, or has restarted the Web procedure that brought him or her to the page, the viewstate is recreated and everything works just fine. You won t have any trouble until users start using the browser history buttons. If you delete old files or records, say, every three days, then it s hard to imagine that you get into trouble because a user has clicked the Back button without finding the page. Unless your users typically keep the browser window up and running for three days without shutting it down and without the session expiring.

 

Beyond Viewstate

In the end, server-side viewstate may raise more issues than it solves. Don t you think that if keeping the viewstate on the server were a clearly better solution, Microsoft would have coded it this way from the beginning? My opinion is that client-side viewstate is preferable in most cases; but if you want it to stay on the server, then be ready to consider the issues related to user navigation and proliferation of page instances.

 

The viewstate, however, is tightly coupled with the ASP.NET Web Forms model. It makes Web Forms programming extremely convenient and smooth. But this doesn t mean that viewstate is mandatory. ASP.NET programming is possible even without viewstate, and even still in the realm of Web Forms it just requires more attention and a bit more effort on your part.

 

I don t personally know much about the future of the ASP.NET platform, but I do know about the ASP.NET MVC Framework. That framework represents an alternative to Web Forms. And there s no viewstate in it. Take a look at it.

 

Files accompanying this article are available for download.

 

Dino Esposito is an architect at IDesign and specializes mainly in ASP.NET, AJAX, and RIA solutions. Dino is the author of Programming ASP.NET 3.5 Core Reference (Microsoft Press, 2008). He also wrote Introducing ASP.NET AJAX, also for Microsoft Press. Late-breaking news is available at http://weblogs.asp.net/despos.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish