Switch to full style
Codes,problems ,discussions and solutions
Post a reply

Client-side Caching using AJAX & Javascript

Sun Nov 09, 2008 6:39 am

This article shows you a useful technique to cache some of that page data on the browser in-memory. Since we use Ajax, the page is not reloaded each time. This prevents the cache from being invalidated on each round-trip. Client-side caching is sometimes required for performance reasons and also to take the load off of the DBMS. Since most modern PCs have plenty of RAM to spare, client-side caching becomes an important weapon in the modern programmer's arsenal of tricks.

With the advent of Ajax, application data can be maintained on the client without the use of ViewStates (aka hidden fields) or to a lesser degree, cookies. As any experienced developer will tell you, ViewState is a double-edged sword. Indiscriminate use of ViewStates can really have a detrimental effect on performance - what with the Viewstate payload ferried to and from the server with every postback.

Can we use this technique without Ajax? , the answer is: "No." That is so because we are maintaining the cache as a page-level variable on the client. The moment the page is refreshed via a full-page postback, the cache is invalidated. Keep this in mind while designing your application.

The accompanying code can be extended to use record data too, via Javascript and the presentation logic can also be hived off to a Javascript routine on the client. We could even pipe XML down to the client and it could be parsed and presented through client-side script.

To keep this sample code easy to comprehend, I have rendered the tabular data from within the Web Service itself - which may not be architecturally optimal. Since this is not a treatise on design best-practices, I have taken the liberty of cutting corners here and there for the sake of brevity.

We use JavaScript associative arrays to maintain our client-side cache. One thing to be noted about JS associative arrays is that once you associate a string key with an array, there is no way to iterate through the array using indexes. Supposing you are using it like this: asarray["akey"] = <value> there is no way you can revert to accessing the elements by their ordinal position like this: var anyvar = asarray[0].
Using the code

Let us examine the critical sections of the code that are absolutely necessary for client-side caching to work. The rest of the code deals with presentation and layout and a discussion on these aspects will be skipped.

For those who subscribe to the view that source-code is the ultimate documentation, you may skip this section. For the others, we'll try to separate the wheat from the chaff. Let us look at the most important pieces of code that makes client-side caching with Ajax and Javascript possible. I am using XML data for demonstration purposes and this makes the project run without having to change connection strings or even having SQL Server installed on your computer.
Step 1

Create a New Website and choose "ASP.NET AJAX-Enabled Website". If you do not have .NET 2.0 Ajax Toolkit installed, go here and download them. You cannot begin this exercise without it.
Step 2
Select "Add New Item" by right-clicking on the project in Solution Explorer and choose "Web Service". Add the highlighted code to your web service class. The [ScriptService] attribute decoration makes the web service class callable by the Ajax framework. You have to include using System.Web.Script.Services to be able to do that.

IMPORTANT: You must have installed Microsoft ASP.NET Ajax 1.0 and the Ajax Toolkit for this sample to work. Add a reference to AjaxControlToolkit.dll (not included in the distribution) for the code sample to work You will find it here.

cpp code
using System.Web.Script.Services;
...
[ScriptService]
public class DataAccess : System.Web.Services.WebService
{ ...


Step 3

Flesh out your web method to do something useful. Our example does this:

javascript code
[WebMethod]
public string FetchOrders(string key)
{
return LoadData(key);;
}

private string LoadData(string key)
{
DataSet ds = new DataSet();
ds.ReadXml(this.Context.Server.MapPath("App_Data\\northwind.xml"));
ds.Tables["Orders"].DefaultView.RowFilter = "customerid = '" + key + "'";
return RenderResultTable((ds.Tables["Orders"].DefaultView));
}

private string RenderResultTable(DataView rdr)
{
StringBuilder str = new StringBuilder();
str.AppendLine("<table cellspacing=\"0\" cellborder=\"=0\"
cellpadding=\"5\">");
str.AppendLine("<tr><td><b>Order Date</b></td><td><b>Order ID</b>
</td></tr>");
foreach(DataRowView drv in rdr)
{
str.AppendLine("<tr>");
str.AppendFormat("<td>{0}</td> <td>{1}</td>", drv["OrderDate"],
drv["OrderID"]);
str.AppendLine("</tr>");
}
str.AppendLine("</table>");
return str.ToString();
}


Step 4

Now, we go to the client script that does the actual caching and retrieves data by making a call to the web service. This employs rudimentary JavaScript - no surprises here.
javascript code
var cacheData = new Array(); // This is our cache

var currSel = null;
var seedData = "0BFA4D6B-DD18-48aa-A926-B9FD80BFA5B7";
// Prime a cache item with a unique initial value

Step 5

Add the needed JavaScript code now. We have a HTML button with the ID btnFetch. In the OnClick event, we trigger either an Ajax asynchronous call to the server or render from the cache.

javascript code
function btnFetch_onclick() 
{
currSel = document.getElementById("dlCustomers").value;
var status = document.getElementById("divStatus");
//var svc = new DataAccess();


if(cacheData[currSel]==null)
{
DataAccess.FetchOrders(currSel,OnCompleteCallback,OnErrorCallback,
OnTimeOutCallback);
cacheData[currSel] = seedData;
status.innerHTML = "[Live Result]";
}
else
{
status.innerHTML = "[Cached Result]";
var cacheobject = FetchDataFromCache(currSel);
RenderData(cacheobject);
}
document.getElementById("dlCustomers").focus();
}


Screenshot - article1.gifOne point to take careful note of is that if you are using multiple cache arrays in your page, you will have to maintain multiple states for the item currently selected. In that case, you might have additional state variables like currProductSel, currCustSel etc. Of course, you will have to have multiple cache arrays as well


If employed correctly, client-side caching can be a great tool to enhance the user experience and make the web application more responsive. If your application is so designed, you may also consider prefetching data through Ajax calls and plonking them into the cache. Prefetching requires minimal code overhead and can be an additional boost to performance if your application warrants it.
csharp code
using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.Script.Services;
using System.Text;
using System.Threading;


/// <summary>
/// Summary description for DataAccess
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class DataAccess : System.Web.Services.WebService
{

public DataAccess ()
{

//Uncomment the following line if using designed components
//InitializeComponent();
}

[WebMethod]
public string FetchOrders(string key)
{
//SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString);
////SqlCommand cmd = new SqlCommand("select CONVERT(CHAR(10), orderdate, 101) as OrderDate, OrderID from orders where customerid = @custid", conn);
//SqlCommand cmd = new SqlCommand("select CONVERT(CHAR(10), orderdate, 101) as OrderDate,customerid, OrderID from orders", conn);
//SqlParameter parm = new SqlParameter("@custid", key);
//cmd.Parameters.Add(parm);
//conn.Open();
//SqlDataReader rdr = cmd.ExecuteReader();
//DataSet ds = new DataSet();
//ds.Load(rdr, LoadOption.OverwriteChanges, new string[] { "Orders" });
//ds.WriteXml("c:\\orders.xml");
////return FormatHTMLTable(rdr);
//return "hello";
string results = LoadData(key);
return results;
}
private string RenderResultTable(DataView rdr)
{
StringBuilder str = new StringBuilder();
str.AppendLine("<table cellspacing=\"0\" cellborder=\"=0\" cellpadding=\"5\">");
str.AppendLine("<tr><td><b>Order Date</b></td><td><b>Order ID</b></td></tr>");
foreach(DataRowView drv in rdr)
{
str.AppendLine("<tr>");
str.AppendFormat("<td>{0}</td> <td>{1}</td>", drv["OrderDate"], drv["OrderID"]);
str.AppendLine("</tr>");
}
str.AppendLine("</table>");
return str.ToString();
}

private string LoadData(string key)
{
DataSet ds = new DataSet();
ds.ReadXml(this.Context.Server.MapPath("App_Data\\northwind.xml"));
//DataView dv = ds.Tables["Orders"].DefaultView;
//dv.RowFilter = "customerid = '" + key + "'";
ds.Tables["Orders"].DefaultView.RowFilter = "customerid = '" + key + "'";
return RenderResultTable((ds.Tables["Orders"].DefaultView));
}

}


Other sample :
html code
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Client-side Caching using AJAX</title>
<link href="Style.css" rel="stylesheet" type="text/css" />

<script language="javascript" type="text/javascript">
// <!CDATA[

var cacheData = new Array();
var currSel = null;
var seedData = "0BFA4D6B-DD18-48aa-A926-B9FD80BFA5B7";
function btnFetch_onclick()
{
currSel = document.getElementById("dlCustomers").value;
var status = document.getElementById("divStatus");
//var svc = new DataAccess();

if(cacheData[currSel]==null)
{
DataAccess.FetchOrders(currSel,OnCompleteCallback,OnErrorCallback, OnTimeOutCallback);
cacheData[currSel] = seedData;
status.innerHTML = "[Live Result]";
}
else
{
status.innerHTML = "[Cached Result]";
var cacheobject = FetchDataFromCache(currSel);
RenderData(cacheobject);
}
document.getElementById("dlCustomers").focus();
}

function FetchDataFromCache(objectkey)
{
return cacheData[objectkey];
}

function RenderData(data)
{
var cndiv = document.getElementById("ajaxContent");
cndiv.style.display = "";
cndiv.innerHTML = String(data);
}

function OnCompleteCallback(retResult)
{

if(cacheData[currSel]==seedData)
{
cacheData[currSel]=retResult;
}
RenderData(retResult);
}

function OnTimeOutCallback(retResult)
{
alert ("AJAX Callback Timeout occurred.");
}

function OnErrorCallback(retResult)
{
alert ("AJAX Callback Error occurred.");
}

function OnBodyLoad()
{
document.getElementById("dlCustomers").focus();
}

function OnKeyPress()
{
if(event.keyCode == 13)
btnFetch_onclick();
}
// ]]>
</script>
</head>
<body style="text-align:center;" onload="OnBodyLoad()" onkeypress="return OnKeyPress();">
<form id="form1" style="text-align:center;" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" AsyncPostBackTimeout="0" ScriptLoadTimeout="0" ScriptMode="Release" >
<Services>
<asp:ServiceReference Path="DataAccess.asmx" />
</Services>
</asp:ScriptManager>
<div id="MainContainer" style="float:left; width: 593px; height: 252px;" class="Container">
<h3 style="text-align: center">Using Associative Arrays for Client-side Caching using AJAX &amp; Javascript</h3>
<div id="MainContent" style="float:left;width:200px;text-align:left;">
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="Label1" runat="server" Text="Select a Customer:"></asp:Label>
<asp:DropDownList ID="dlCustomers" runat="server" Width="198px">
</asp:DropDownList>
</ContentTemplate>
</asp:UpdatePanel>
<input type="button" style="margin-top:10px; height: 24px; width: 72px;" value="Fetch" id="btnFetch" onclick="return btnFetch_onclick()" />
<br />
<span id="divStatus" style="width: 198px; height: 19px;color:Navy;"></span><br />
</div>
<asp:Panel ID="ajaxContent" runat="server" CssClass="DivTable">
</asp:Panel>&nbsp;
</div>
<cc1:DropShadowExtender ID="DropShadowExtender1" runat="server" Opacity="0.2" TargetControlID="ajaxContent"
Width="3">
</cc1:DropShadowExtender>
</form>
</body>
</html>



Attachments
article_src.zip
(13.35 KiB) Downloaded 1172 times

Post a reply
  Related Posts  to : Client-side Caching using AJAX & Javascript
 pre caching using ajax     -  
 Ajax Source code to Suggest application with JSP Server side     -  
 remove space ,trim strings from left side ,right side ,both.     -  
 show figures side by side using subfigure     -  
 Ajax without javascript     -  
 Can I display same vertically on right side?     -  
 Need Help with Server side scriping in Java     -  
 client server client     -  
 What is AJAX, How to start AJAX?     -  
 FTP Server and FTP Client     -  

Topic Tags

AJAX Javascript