The challenge in building high-performance, scalable Web applications is the ability to store items, whether data objects, pages, or parts of a page, in memory the initial time they are requested. You can store these items on the Web server or other software in the request stream, such as the proxy server or browser. This allows you to avoid recreating information that satisfied a previous request, particularly information that demands significant processor time or other resources.
ASP.NET provides caching at several levels for you to leverage and improve the responsiveness of your application by storing the page output or application data across HTTP requests and reuse it. This allows the web server to take advantage of processing the request without recreating the information and thus saving time and resources.
Caching Opportunities in ASP.NET web pages
ASP.Net supports both page (or portion of a page) caching and also caching data from a backend data source and storing these individual objects in memory.
To achieve this following features are provided in ASP.NET
- Page output caching
- Page fragment caching
- Data caching
Page Output Caching
Dynamically generated .aspx pages can be cached for efficiency instead of re-generating each .aspx page for identical requests, the pages are cached.
Page Output caching can be achieved in the following 3 ways.
1) This can be achieved by specifying the @OutputCache directive at the top of the ASP.Net page. It controls the caching duration (in seconds).
<%@ OutputCache Duration="3600" VaryByParam="none" %>
<html>
<script language="C#" runat="server">
void Page_Load(Object sender, EventArgs e)
{
msg.Text = DateTime.Now.ToString();
}
</script>
<body>
<h3>Output Cache example</font></h3>
<p>Last generated on: <asp:label id="msg" runat="server"/>
</body>
</html>
2) Programmatically you can achieve this using the HttpCachePolicy sealed class which can be accessed from the HttpResponse.Cache property of the Page.Response property.
HttpCachePolicy class
public sealed class HttpCachePolicy
{
public HttpCacheVaryByHeaders VaryByHeaders {get;}
public HttpCacheVaryByParams VaryByParams {get;}
public void AppendCacheExtension(string extension);
public void SetCacheability(HttpCacheability cacheability);
public void SetExpires(DateTime date);
public void SetLastModified(DateTime date);
public void SetMaxAge(TimeSpan delta);
public void SetNoServerCaching();
public void SetSlidingExpiration(bool slide);
//...
}
Modifying a page's caching policy programmatically.
<html>
<script language="C#" runat="server">
void Page_Load(Object sender, EventArgs e)
{
Response.Cache.SetExpires(DateTime.Now.AddSeconds(360));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetSlidingExpiration(true);
msg.Text = DateTime.Now.ToString();
}
</script>
<body>
<h3>Output Cache example</font></h3>
<p>Last generated on: <asp:label id="msg" runat="server"/>
</body>
</html>
3) Caching of GET requests with query strings or POST requests with bodies controlled by VaryByParam property. It determines how many versions of the page are cached. Specifying it to "none" means that only GET requests with NO query strings or POST requests with NO body will hit the cache. Specifying VaryByParam to "*" means that as many different querystring or POST body requests are received
will be cached.
The following table gives you a brief idea of the VaryByParam values.
VaryByParam |
Description |
none |
One version of page cached (only raw GET) |
* |
n versions of page cached based on query string and/or POST body |
V1 |
n versions of page cached based on value of V1 variable in query string or POST body |
V1;V2 |
n versions of page cached based on value of V1 and V2 variables in query string or POST body |
<%@ OutputCache Duration="60" VaryByParam="none" %>
<%@ OutputCache Duration="60" VaryByParam="*" %>
<%@ OutputCache Duration="60" VaryByParam="name;age" %>
The first time the page is requested, the response is generated and added to the cache. If the page is requested within 60 seconds with the same values for name and age, then the cached version is used.
For Information:
Other cache varying options
The OutputCache directive supports several other cache varying options
- VaryByHeader - maintain separate cache entry for header string changes (UserAgent, UserLanguage, etc.)
- VaryByControl - for user controls, maintain separate cache entry for properties of a user control
- VaryByCustom - can specify separate cache entries for browser types and version or provide a custom GetVaryByCustomString method in HttpApplication derived class.
Page fragment caching
Parts of the ASP.Net page which are to be cached are encapsulated in Web Forms User Controls.
MyUserControl.ascx
<%@ OutputCache Duration="60" VaryByParam="none" %>
<%@ Control Language=C# %>
<script runat=server>
protected void Page_Load(Object src, EventArgs e)
{
m_Date.Text = "Control generated at " +DateTime.Now.ToString();
}
</script>
<asp:Label id=m_Date runat=server />
Client.aspx
<%@ Page Language=C# %>
<%@ Register TagPrefix="DM" TagName="UserFrag"src="MyUserControl.ascx" %>
<html>
<script runat=server>protected void Page_Load(Object src, EventArgs e)
{
m_PageDate.Text = "Page generated at " +DateTime.Now.ToString();
}
</script>
<body>
<DM:UserFrag runat=server ID="Userfrag1" NAME="Userfrag1"/><br>
<asp:Label id=m_PageDate runat=server />
</body>
</html>
Data caching
In simple terms data caching is storing data in memory for quick access. Typically information that is costly to obtain (in terms of performance) is stored in the cache. One of the more common items stored in a cache in a Web application environment is commonly displayed database values; by caching such information, rather than relying on repeated database calls, the demand on the Web server and database server's system resources are decreased and the Web application's scalability increased.
ASP.NET provides a full-featured cache engine that can be used by pages to store data across HTTP requests
A small example of storing the value obtained from the database is given below.
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<html>
<script language="C#" runat="server">
protected void Page_Load(Object src, EventArgs e)
{
DataView dv = (DataView)Cache.Get("EmployeesDataView");
if (dv == null)
{
// wasn't there
SqlConnection conn =new SqlConnection("server=localhost;uid=sa;pwd=;database=Test");
SqlDataAdapter da =new SqlDataAdapter("select * from Employees", conn);
DataSet ds = new DataSet();
da.Fill(ds, "Employees");
dv = ds.Tables["Employees"].DefaultView;
Cache.Insert("EmployeesDataView", dv);conn.Close();
}
else
Response.Write("<h2>Loaded employees from data cache!</h2>");
lb1.DataSource = dv;
lb1.DataTextField = "Name";
lb1.DataValueField = "Age";
DataBind();
}
</script>
<body>
<asp:ListBox id="lb1" runat=server />
</body>
</html>
Cache entry attributes
When adding cache entries, several attributes can be specified
- Dependencies (on files, directories, or other cache entries)
- Absolute expiration time
- Sliding expiration time
- Relative priority
- Rate of priority decay
- Callback function for removal notification
public void Insert( string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback );
The different parameters which the Insert method accepts is given below
Parameters |
key |
The cache key used to reference the object. |
value |
The object to be inserted in the cache. |
dependencies |
The file or cache key dependencies for the item. When any dependency changes, the object becomes invalid and is removed from the cache. If there are no dependencies, this parameter contains a null reference. |
absoluteExpiration |
The time at which the inserted object expires and is removed from the cache. |
slidingExpiration |
The interval between the time the inserted object was last accessed and when that object expires. If this value is the equivalent of 20 minutes, the object will expire and be removed from the cache 20 minutes after it was last accessed. |
priority |
The cost of the object relative to other items stored in the cache, as expressed by the CacheItemPriority enumeration. This value is used by the cache when it evicts objects; objects with a lower cost are removed from the cache before objects with a higher cost. |
onRemoveCallback |
A delegate that, if provided, will be called when an object is removed from the cache. You can use this to notify applications when their objects are deleted from the cache. |
Removing objects from the cache
- Objects can be explicitly taken out of the cache by calling Remove
- Cache can remove item implicitly for a variety of reasons
- Data expiration
- Memory consumption
- Low priority data removed first
- Values marked with Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, or CacheItemPriorityDecay.Never are never removed
- Can register for removal notification, including reason
Using removal notification callback
<script language=C# runat=server>
public void Application_OnStart()
{
System.IO.StreamReader sr =new System.IO.StreamReader("pi.txt");
string pi = sr.ReadToEnd();
Context.Cache.Add("pi", pi, null,Cache.NoAbsoluteExpiration,new TimeSpan(0, 5, 0),CacheItemPriorityDecay.Never,new CacheItemReomovedCallback(this.OnRemove));
}
public void OnRemove(string key, object val, CacheItemRemovedReason r)
{
// respond to cache removal here
}
</script>
Disadvantages of Caching
Although the caching support can be really helpful in a lot of scenarios, it has some major disadvantages:
· With cached pages that display results retrieved from a database the cache can be inconsistent, i.e. not reflecting the latest changes applied to a database.
Conclusion
Caching dramatically improves the performance of a web site, but it has disadvantages as well. The user should take care of the parameter values for expiration policy.