Dynamic Content Substitution usi
Dynamic Cache Substitution using Substitution Control
Introduction
ASP.NET Output Caching features help us to develop performance oriented web
sites. No doubt that output caching reduces server side event processing.
However, in the process it bypasses the entire server side processing. That
means after the first request the page becomes static in terms of its contents
for the entire cache duration. In some situations this architecture poses
problems of its own. Recognizing these problems Microsoft introduced a new web
server control called Substitution control in ASP.NET 2.0. The Substitution
control allows you to dynamically change a part of the entire cached output.
This substitution happens on the server and you can control what to substitute
in the output.
Sample Scenario
In order to understand the problem behind normal output caching behavior we
will develop a simple application.
Create a new web site using VS.NET. Add two web forms - Article.aspx and
Login.aspx. Design Artile.aspx as shown below:

The application is protected using Forms authentication. At the top there is
a welcome message displayed for the logged in user. Below there is a LinkButton
for signing out. Finally the article content is displayed. The article content
is stored in an XML file (in real world cases it will come from database).
Similarly design Login.aspx as shown below:

The Login.aspx simply contains a TextBox for entering user name. For the sake
of simplicity we have skipped the user authentication logic.
Enabling forms authentication
Now, open web.config file and add the following markup to it.
<authentication mode="Forms"/>
<authorization>
<deny users="?"/>
</authorization>
Here, we set the authentication mode to Forms. Note that default URL for
login page is Login.aspx and hence we need not configure it separately. We also
blocked anonymous uses using <authorization> tag.
Now open Login.aspx and add the following code in the Click event handler of
Submit button.
protected void Button1_Click(object sender, EventArgs e)
{
FormsAuthentication.SetAuthCookie(TextBox1.Text, false);
Response.Redirect("article.aspx");
}
We call SetAuthCookie() method of FormsAuthentication class. The
SetAuthCookie() method issues an authentication cookie to the user. In read
world scenarios you will execute some database driven logic before issuing the
authentication cookie. The first parameter of SetAuthCookie() method represents
the user name. The second parameter indicates whether to persist the
authentication cookie even if browser windows is closed or not. After issuing
the authentication cookie the user is redirected to the Article.aspx page.
Note: Discussing forms authentication is out of the scope of this article. If
you wish to learn forms authentication in detail refer the following articles.
Forms
Authentication in ASP.NET
Applying
Forms Authentication Selectively
Forms
Authentication Against An XML File
Membership, Roles and Profile - An Example (Part 1)
Membership, Roles and Profile - An Example (Part 2)
Creating an XML file to store article contrnt
Add an XML file to your web site and key in the following markup:
<?xml version="1.0" encoding="utf-8" ?>
<articles>
<article>
<title>.NET The Great</title>
<author>Bob</author>
<content>
<![CDATA[article body goes here]]>
</content>
</article>
</articles>
Save the file as Article.xml. The Article.xml file supplies the content of
the article. The tag names used are self explanatory and need no elaboration.
Displaying the article
Open Article.aspx in the IDE and design it as shown above. Write the
following code in the Page_Load event handler.
protected void Page_Load(object sender, EventArgs e)
{
Label2.Text = User.Identity.Name + " !";
DataSet ds = new DataSet();
ds.ReadXml(Server.MapPath("article.xml"));
Label4.Text=ds.Tables[0].Rows[0]["title"].ToString();
Label5.Text="By " + ds.Tables[0].Rows[0]["author"].ToString();
Label6.Text=ds.Tables[0].Rows[0]["content"].ToString();
}
The code assigns the user name of the currently logged in user to a label.
This is done with the help of User.Identity.Name property. This way the end user
sees the welcome message in the browser. It then creates a DataSet and reads
Article.xml file into it using ReadXml() method. The ReadXml() file accepts the
path of the XML file to read and generates DataTable(s) in the DataSet. Because
of the nesting of our file it will create only one DataTable with three columns
- title, author and content. The column values are retrieved from the DataSet
and assigned to the respective labels.
In the Click event handler of Sign Out button add the following code:
protected void LinkButton1_Click(object sender, EventArgs e)
{
FormsAuthentication.SignOut();
Response.Redirect("login.aspx");
}
The code simply calls the SignOut() method of FormsAuthentication class. This
method removes the authentication cookie that we issued initially. The user is
then taken to the login page.
Finally, enable caching for Article.aspx by adding @OutputCache directive as
shown below:
<%@ OutputCache Duration=60 VaryByParam=None %>
The @OutputCache directive sets the duration of caching to 60 seconds.
Now we are ready to see the problem!
Testing the application and knowing the problem
Run the Login.aspx. Enter some user name (say User1) and click on submit
button. You will be taken to the Article.aspx page and a welcome message will be
displayed (say Welcome User1). Now click on Sign Out button so that you will be
signed out and taken to login page again. Enter another user name (say User2)
and click on submit button. You will be taken to the Article.aspx but this time
welcome message doesn't change. It still shows the previous user name. That's
the problem! Since @OutputCache directive caches the entire page output even the
user name is cached. This is certainly not what we want. Thankfully the
Substitution control helps to resolve the problem.
Substitution control to the rescue
Let's modify Article.aspx to use Substitution control. Drag and drop a
Substitution control from toolbox below the welcome label.
Go in the code behind and create a static method called GetUserName. The
method is shown below:
public static String GetUserName(HttpContext context)
{
return "<h3>Welcome " + context.User.Identity.Name + " !</h3>";
}
This method is responsible for generating the content that is to be
substituted dynamically. The method signature must match following rules:
- It must be defined as a static method
- It must accept a parameter of type HttpContext
- The return type must be string
Inside the method you can not access any controls or class level instance
variables but you can access objects such as Request, Response and User via the
HttpContext parameter. In our example the GetUserName method returns the user
name of currently logged in user i.e the content that we want to change
dynamically.
This method is linked with the Substitution control via a property called
MethodName. The MethodName property of Substitution control specifies the name
of the method that will be invoked when the request is made to the page. The
method of course must follow the above rules.
Run the Article.aspx page again and test it as before. You will find that the
welcome label continues to show User1 but the Substitution control correctly
displays the newly entered user name. The following Figure shows a sample run of
Article.aspx with Substitution control.

Thus the Substitution control helps us to dynamically change part of the
total cached page. Though we have seen the use of Substitution control for a web
form the same concept is applicable for Web User Controls also.