Are You Sure You Want Server-side Viewstate?

Can You Believe It Raises Scalability Issues?

Dino Esposito

October 30, 2009

10 Min Read
ITPro Today logo in a gray background | ITPro Today

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 thatalways stirs developers interest: server-side viewstate (see Server ViewstateRevisited). In particular, I focused on a couple of aspects. First, theserialization algorithm that silently changed in transition from ASP.NET1.x to ASP.NET 2.0 mitigates the impact ofthe problem simply by making the size of the viewstate field significantlysmaller. Some 40% of reduced viewstate, on average, is definitely a good resultfor everybody. Second, for those still willing to keep the viewstate field offtheir client pages, a new persistence algorithm has been developed mostly dueto the internal refactoring of the viewstate management process within the Pageclass.

 

Keeping the viewstate content on the server, though, is amuch less appealing trick than it may appear at first glance. You can t simplylimit it to maintain one copy of the viewstate per page. If this sounds like aweird constraint, think of a user who requests the same page several times inthe same session. For the sake of consistency, you should create a new file orrecord for each page request and ensure you employ a proper naming conventionto prevent overriding page state. What if the user navigates back to thepreviously visited page? You must be able to retrieve the viewstate of exactlythat instance of the page.

 

With a hidden field in the folds of the client page, theproblem is brilliantly solved in a way that doesn t affect the server. If a server-side storage for the viewstate is wanted, then it isup to you to manage the issue of multiple page instances. This means thateither you put an upper limit to the number of page instances that can bemaintained (thus limiting the browser s history navigation) or you accept apotential scalability risk. The proliferation of server-side viewstate copieseats up memory and server resources. The viewstate is implicit in the WebForms model; it simply can t be dropped as an unnecessary evil. It is atrade-off, though, and at the end of the day, the client hidden field is thelesser evil. If you still want to keep the viewstate on the server, though,there s another, often overlooked, issue to deal with: What naming conventionwould 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 copyof 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 storeviewstate copies on the server s file system, out-of-process memory, or adatabase, you still have to come up with an effective naming convention. Inother words, you need an algorithm to bind the viewstate content to aparticular page instance.

 

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

 

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

 

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

 

Revisiting Server-side Viewstate

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

 

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

 

public class ServerViewStatePage : Page

{

   privatePageStatePersister _persister = null;

   publicServerViewStatePage()

   {

   }

   protected overridePageStatePersister PageStatePersister

   {

       get

       {

           if(this._persister == null)

           {

               this._persister = new MyOwnPageStatePersister(this);

           }

           returnthis._persister;

       }

   }

}

Figure 1: A custompage that saves viewstate on the server.

 

public class MyOwnPageStatePersister : PageStatePersister

{

   publicMyOwnPageStatePersister(Page page) : base(page)

   {

   }

   public override voidLoad()

   {

       string pageState =String.Empty;

        // Get it fromstore

       pageState =ViewStatePersistenceLayer.Load(base.Page);

       // Attach viewstateto the page

       if(!string.IsNullOrEmpty(pageState))

       {

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

           base.ViewState= pair.First;

           base.ControlState = pair.Second;

       }

   }

   public override voidSave()

   {

       string pageState =String.Empty;

       // Get control andview state

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

       {

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

              base.ControlState));

       }

       // Save it offsomewhere

       ViewStatePersistenceLayer.Save(base.Page, pageState);

       return;

   }

}

Figure 2: A custompersister class using a disk file.

 

The idea is to associate each page instance with a uniquevalue; for example, a GUID. This value is saved to a custom hidden field withinthe page and takes up only a few bytes. The page saves to a location theviewstate to the server, be it a table record or a disk file, identified by thekey value. This way, you can manage on the server any number of instances pereach page URL your users visit. Here s a brief excerpt from the Save method ofthe 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 Figure3.

 

public class ViewStatePersistenceLayer

{

   private const stringHIDDENFIELDNAME = "__VSSERVERKEY";

   public static voidSave(Page pageInstance, string pageState)

   {

       // Get the key tostore the viewstate for THIS page instance

       string key = GenerateKey(pageInstance);

       // Save to a diskfile

       string fileName =GetFileName(pageInstance, key);

       StreamWriter writer= new StreamWriter(fileName);

       writer.Write(pageState);

       writer.Close();

   }

    public static string Load(Page pageInstance)

   {

       // Get the key toretrieve the viewstate for THIS page instance

       string key =FindViewStateKey(pageInstance);

       // Get viewstatefrom disk file

       string file =GetFileName(pageInstance, key);

       StreamReader reader= new StreamReader(file);

       string pageState =reader.ReadToEnd();

       reader.Close();

       return pageState;

   }

   private static stringGetFileName(Page pageInstance, string key)

   {

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

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

       return fileName;

   }

   private static stringFindViewStateKey(Page pageInstance)

   {

       string key =String.Empty;

       object o =pageInstance.Request[HIDDENFIELDNAME];

       if (o != null)

           key =(string)o;

       return key;

   }

   private static stringGenerateKey(Page pageInstance)

   {

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

       // Register thehidden field

       pageInstance.ClientScript.RegisterHiddenField(HIDDENFIELDNAME, key);

       return key;

   }

}

Figure 3: Theviewstate persistence layer.

 

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

 

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

pageInstance.ClientScript.RegisterHiddenField(

 HIDDENFIELDNAME, key);

 

When the ASP.NETruntime 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 Loadmethod of our custom viewstate persistence layer. Here, the content of the adhoc hidden field is retrieved the key value and used to access theviewstate content to use.

 

Can You Spot the Problem?

As the sample code that accompanies this articledemonstrates, you can successfully hold the viewstate on the server and stillnavigate between page instances as you expect. For each request, the amount ofdata being downloaded and uploaded is significantly reduced. For example, byholding the viewstate on the server in a sample page that contains hundreds ofrecords in a data grid, you reduce page traffic by 50%. Is that all? No darksides? No drawbacks?

 

Whether you store the server viewstate as records of adatabase table or files on the disk, you have no easy way to detect when arecord has to be removed. On the other hand, if you don t implement aneffective 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 avariation of the mechanism used by the ASP.NETruntime to manage its temporary files. Periodically, the runtime accesses theinternal folders on the server in sweep mode and clears some files those thatit reckons safe to delete. As this is an internal aspect of the ASP.NETengine, its details are not relevant here. However, it gives you an idea ofwhat you should do to keep the natural proliferation of records or server filesunder control. You can run a scheduled job that clears files created a few daysor hours before. Alternatively, this job can be done by the same GenerateKeymethod you use in the viewstate persistence layer.

 

Finally, note that if a page at some point can t find therecord or file it is looking for, it won t be able to restore viewstate. Isthis a problem? Absolutely; it is the source of some trouble for the user, butit is not necessarily a big deal. Once the user has reloaded the page from thehome, 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 anytrouble until users start using the browser history buttons. If you delete oldfiles or records, say, every three days, then it s hard to imagine that you getinto trouble because a user has clicked the Back button without finding thepage. Unless your users typically keep the browser window up and running forthree days without shutting it down and without the session expiring.

 

Beyond Viewstate

In the end, server-side viewstate may raise more issuesthan it solves. Don t you think that if keeping the viewstate on the serverwere a clearly better solution, Microsoft would have coded it this way from thebeginning? My opinion is that client-side viewstate is preferable in mostcases; but if you want it to stay on the server, then beready to consider the issues related to user navigation and proliferation ofpage instances.

 

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

 

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

 

Files accompanyingthis article are available for download.

 

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

 

 

 

Read more about:

Microsoft
Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like