5,427,813 members and growing! (17,977 online)
Email Password   helpLost your password?
Web Development » ASP.NET » General     Intermediate

Automatically Saving Web Form Data

By Usman Shaheen

how to automatically save user input in web applications
C#, Windows, .NET, Visual Studio, ASP.NET, Dev

Posted: 29 Jun 2006
Updated: 24 Sep 2006
Views: 30,150
Bookmarked: 42 times
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
13 votes for this Article.
Popularity: 4.64 Rating: 4.17 out of 5
0 votes, 0.0%
1
2 votes, 15.4%
2
1 vote, 7.7%
3
4 votes, 30.8%
4
6 votes, 46.2%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

 

Introduction

This article explains how to automatically save the user input in background whilst the user is filling forms. This is particularly useful for large forms and you don't miss the data if user closes the Browser without saving the form, navigate away from the web page or the browser crashes. The technique used here can also be used to implement roaming user session for an authenticated web user, i.e. the user rebinds to its existing session if he logs in again prior to session expiration - even if logs in from another system.
This functionality is somewhat similar to ASP.NET 2.0 profiling.

The Idea

The idea is to send user data to web server on periodic basis where it is stored in memory and later flushed to database for persistent storage. 

JavaScript is used to monitor the user input in browser and fill in a hash table with user input. On specified periods, the hash table is serialized as querystring and sent to an aspx page (AutoSave.aspx) using xmlhttp object. AutoSave.aspx populates an in memory object with the values from the Query String. After a specified period, usually at session timeout, the values are flushed to database.

The Implementation

 1. Bind onblur event of all input controls to populate hash table when user enters data in form.
 2. Submit user data to server by calling AutoSave() function. xmlHttp wrapper implementation is from http://www.codeproject.com/Ajax/AJAXWasHere-Part1.asp

3. Call AutoSave just before browser is going to close by the user, i.e. window.onbeforeunload event.

WebForm1.aspx
<script defer="defer" language="javascript"> bindEvents(); //binds onblur events, onchange for DropDown Lists var xmlHttp; xmlHttp = GetXmlHttpObject(CallbackMethod); function AutoSave() { if (!Data.isEmpty()) { qstring = Data.toQueryString(); SendXmlHttpRequest(xmlHttp, "AutoSave.aspx?" + qstring.substring(0,qstring.length-1)); Data.clear(); } } function CallbackMethod() { try { //readyState of 4 or 'complete' represents if (xmlHttp.readyState == 4 || xmlHttp.readyState == 'complete') { var response = xmlHttp.responseText; if (response.length > 0) { alert("Unable to Auto Save Data. Please check your internet connectivity"); } } } catch(e){} } window.setInterval(AutoSave, 15000); window.onbeforeunload = AutoSave; </script >
4. Using Hash Table Implementation in JavaScript by Michael Synovic

WebForm1.aspx
<script defer="defer" language="javascript">


/*Bind event with Controls */ function bindEvents(){ var textBoxes = document.getElementsByTagName("input"); for (i=0; i< textBoxes.length; i++){ if (textBoxes[i].type == 'text' || textBoxes[i].type == 'radio'){ textBoxes[i].onblur = updateHashTable; } } for (i=0; i< textBoxes.length; i++){ if (textBoxes[i].type == 'checkbox'){ textBoxes[i].onblur = updateHashTableforCheckBox; } } var comboBoxes = document.getElementsByTagName("select"); for (j=0; j< comboBoxes.length; j++){ comboBoxes[j].onchange = updateHashTableforCombo; } } var Data= new Hashtable(); function updateHashTable(){ Data.put(this.id, this.value); } function updateHashTableforCheckBox(){ Data.put(this.id, this.checked); } function updateHashTableforCombo(){ Data.put(this.id, this.options(this.selectedIndex).value); } </script>

5. First we check if the DTO is already there, i.e. we've user session that is not yet expired, bind with the existing in-memory DTO, otherwise, create a new object in Cache to hold user data. Cache is used instead of Session object because it can survive across user sessions/logins and we can use Callback method to emulate Session_End event. CacheExpired method is invoked after Cache timeout and flushes the data to database.

WebForm1.aspx.cs

private void Page_Load(object sender, System.EventArgs e){ if (!Page.IsPostBack){ UserData userData; if (Cache[Context.User.Identity.Name] == null){ userData = new UserData(); Cache.Insert(Context.User.Identity.Name, userData ,null, Cache.NoAbsoluteExpiration,
TimeSpan.FromMinutes(Session.Timeout),
CacheItemPriority.Default, new CacheItemRemovedCallback(CacheExpired)); } else{ userData = Cache[Context.User.Identity.Name] as UserData; }
FillPage(userData); //to populate web form controls with values from DTO }
}
internal void CacheExpired(string key, object val, CacheItemRemovedReason reason) { if (reason != CacheItemRemovedReason.Removed){

//Save.aspx invokes userData.updateDB() to update data in database HttpContext.Current.Server.Execute("Save.aspx", new StringWriter()); } }

6. On AutoSave.aspx, we iterate through the Query String values and set the properties of DTO and update the DTO object in memory.
AutoSave.aspx.cs
private void Page_Load(object sender, System.EventArgs e) { UserData userData = Cache[Context.User.Identity.Name] as UserData; for(int i=0; i < Request.QueryString.Count; i++){ try{ userData[Request.QueryString.GetKey(i)] = Request.QueryString.Get(i); } catch (Exception ex){ continue; } } Cache[Context.User.Identity.Name] = userData; }

7. A DTO (Data Transfer Object) is defined to hold the user data in memory. A string indexer is implemented to directly assign the values to class properties from Querystring.

 8. updateDB() method is to flush the data from memory to database. updateDB()is invoked only when user Submits the form or Cached  DTO is expired.

 

DTO.cs
public class UserData { public string this[string paramName]{// string indexer get { return this.GetType().GetProperty(paramName).GetValue(this, null).ToString(); } set{ this.GetType().GetProperty(paramName).SetValue(this,value,null); } } private string _LastName; public string LastName { get { return _LastName; } set { _LastName = value; } } private string _FirstName; public string FirstName { get { return _FirstName; } set { _FirstName = value; } } private string _Email; public string Email { get { return _Email; } set { _Email = value; } } public void updateDB() { /***here: invoke Stored Procedure to update data in database***/ HttpContext.Current.Cache.Remove(HttpContext.Current.User.Identity.Name); }

Limitations

1. The solution won't work if web page is automatically filled in client browser by some web-form-filler software.

2. If the user input fields are defined inside user controls (ascx) that are reused in many places, we might need to modify the solution a bit as currently we 've  name-mapping between data values to DTO properties.

History

first release 6/29/2006. 
update 9/25/2006. 

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

About the Author

Usman Shaheen


Usman is living in Riyadh, Saudi Arabia, working as BizTalk Consultant at Arab national Bank.
Programming experience includes ASP.NET, C#, Ajax, JavaScript, MS SharePoint Portal Server, MS BizTalk Server
www.linkedin.com/in/usmanshaheen

Occupation: Web Developer
Location: Pakistan Pakistan

Other popular ASP.NET articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 5 of 5 (Total in Forum: 5) (Refresh)FirstPrevNext
Subject  Author Date 
GeneralUse intellilogin to protect your web login forms.memberHoochie18:19 16 Oct '06  
GeneralYou dont have to use a database!memberzygan22:11 21 Aug '06  
GeneralRe: You dont have to use a database!memberUsman Shaheen3:14 22 Aug '06  
QuestionNo downloadable samplesmemberCatalin M.14:26 11 Aug '06  
GeneralThanks for sharing your workmemberBrian Leach9:19 5 Jul '06  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 24 Sep 2006
Editor:
Copyright 2006 by Usman Shaheen
Everything else Copyright © CodeProject, 1999-2008
Web20 | Advertise on the Code Project