search
Japanese Chinese Nederlands Espanol Italiano Deutsch Francais Twitter Rss Feeds
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsFAQs
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Operating SystemsArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

ASP.NET AJAX Automatically Saving Web Form Data


By usman shaheen
Printer Friendly Version
View My Articles
54 Views
    

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


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. 



button
Article Discussion: Automatically saving web form data
usman shaheen posted at Monday, September 25, 2006 5:55 AM
Original Article