Building Microsoft ASP.NET Applications for Mobile Devices, Second Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Building Microsoft ASP.NET Applications for Mobile Devices, Second Edition [Electronic resources] - نسخه متنی

Andy Wigley; Peter Roxburgh

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
توضیحات
افزودن یادداشت جدید






Caching

ASP.NET provides three methods for caching that you can use to improve performance:



Output cachingThe runtime caches the entire output of the page for a specific period of time.



Fragment cachingThe runtime caches the output of a user control.



Data cachingPages can use this dictionary-structured cache to store arbitrary objects across HTTP requests.



Let's take a look at each of these caching methods.


Using Output Caching


You can cache a page by using an @ OutputCache directive at the head of the mobile Web Forms page, as you see here:

<%@ OutputCache Duration="60" VaryByParam="none"%>

The Duration attribute specifies the number of seconds that IIS should cache the output of this page. The VaryByParam attribute is required but you disable it by setting it to none; we'll look at the use of this attribute in a moment.

With this statement placed at the top of the page, the runtime determines whether to send the cached version of the page or to regenerate the page by comparing the HTTP_User_Agent identification string. The HTTP_User_Agent identification string is sent in the HTTP headers with every request and identifies the type of browser that is on the client. Therefore, if a client requests a page and the runtime has a cached copy of the output for the same type of browser and it hasn't expired, the runtime serves that page from the cache instead of regenerating it. For example, if a device running Microsoft Internet Explorer for the Pocket PC requests a page, the runtime should cache the resulting output and return it only for other devices running Pocket Internet Explorer.

Listing 13-1 shows an example application that has output caching enabled.

Listing 13-1: Page with output caching enabled






<%@ OutputCache Duration="60" VaryByParam="none"%>
<%@ Register TagPrefix="mobile"
Namespace="System.Web.UI.MobileControls"
Assembly="System.Web.Mobile" %>
<%@ Page Inherits="System.Web.UI.MobileControls.MobilePage"
Language="c#"%>
<html>
<head>
<script language="c#" runat="server">
public void Page_Load(Object sender, EventArgs e)
{
lblTime.Text = "Page Loaded at: " + DateTime.Now.ToLocalTime();
}
</script>
</head>
<body
<mobile:Form runat="server" id="frmMain">
<mobile:Label id="lblTime" runat="server"/>
</mobile:Form>
</body>
</html>











When you run the application in Listing 13-1 from a browser and then refresh the display a few times or access it from another browser of the same type, the time displayed doesn't change until 60 seconds after the first access.

As we've said, by default the runtime makes the decision on whether to serve a cached copy of the page output by looking at the HTTP_User_Agent identification string of the request. You can change the default behavior using the VaryByParam, VaryByHeader, and VaryByCustom attributes of the @ OutputCache directive.

The @ OutputCache directive must include the VaryByParam attribute and can include the optional VaryByHeader and VaryByCustom attributes. These attributes have the following meanings:



VaryByParamYou use this mandatory attribute to specify one or more of the values that are posted back from the client to the server, either as a POST parameter or in the query string. If you've set VaryByParam to none (as in Listing 13-1), the runtime will cache only the initial GET request (because this has no POST parameters) or query string (unless the user explicitly enters the URL to fetch with a query string attached—that is, it ends with "?" followed by some data).

In interactive mobile Web applications (the majority), your user typically enters some data, such as a search string or an item selected from a SelectionList, which is used in the server application to make a selection from data, and the selected data is sent back to the browser for display. You'll want to use VaryByParam to make sure that the output is cached based on the user's selections, so you'll enter the names of the POST parameters or query string variables that are used to postback the user's selection to the server. We'll look some more at this technique in the following section, "Using VaryByParam to Cache Based on User Input."

To cache based on more than one parameter, separate the names with a semicolon, as shown here:

<%@ OutputCache Duration="60" VaryByParam="selState;txtSearch" %>

To make your cache selective based on every value specified in the query string or POST parameters rather than specific named items, specify an asterisk for the VaryByParam attribute:

<%@ OutputCache Duration="45" VaryByParam="*" %>



VaryByHeaderThis attribute enables you to cache based on an item in the HTTP headers. For example, to cache based on the Accept-Language header, specify the OutputCache directive this way:

<%@ OutputCache Duration="60" 
VaryByHeader="Accept-Language"
VaryByParam="none" %>



VaryByCustomUse this attribute to vary caching based on the browser type (by using the special value "browser") or based on a custom string that you specify. By default, ASP.NET Mobile Controls automatically applies VaryByCustom="browser", making all caching dependent on the type of browser that makes the request.

You can write custom cache selectors by specifying an arbitrary string for the VaryByCustom attribute and then overriding the GetVaryByCustomString method of the HttpApplication object in the Global.asax file. This method should return a string for the current request, which the runtime then uses to vary caching. For example, the @ OutputCache directive specifying a custom selector named MySelector is coded like this:

<%@ OutputCache Duration="60" 
VaryByCustom="MySelector"
VaryByParam="none" %>

In Global.asax.cs, you would implement the custom selector like this:

public override string GetVaryByCustomString(
HttpContext context, string arg)
{
switch (arg){
case "MySelector":
// Send back the string that is used to distinguish
// between client devices for output caching.
return "MySelector=" + context.Request.Browser
+ context.Request.Frames;
default:
return ";
}
}



Using VaryByParam to Cache Based on User Input


If you've set VaryByParam to none, the runtime will cache only the initial GET request. The application won't cache any subsequent GET requests that include a query string or any parameters on a POST request. In mobile Web applications, the initial GET request of the application is the only element that doesn't involve POST parameters or query strings. Thereafter, any server controls will return the result of user interaction as POST parameters, and the runtime will track sessions through munged URLs, POST parameters, and query strings. In these cases, if you want to enable caching beyond the initial GET request, you must use VaryByParam to specify a parameter or parameters to vary by.

To implement caching that works effectively, you need to identify the particular parameter or parameters that control the returned content—in other words, you must designate the particular server control used to manage the output to the user. Consider the application shown in Figure 13-1. The user selects a state, which the runtime passes back to the server. The server then generates the new output and sends it back to the client.


Figure 13-1: The state selected from the drop-down list that posts back to the server and ultimately creates the new data list

The @ OutputCache directive of this application should specify the POST parameter used to post back the value that the user entered in the SelectionList control. The ASP.NET mobile controls use the control ID when posting back values. In this case, where the SelectionList control has the ID selState, the OutputCache directive reads as follows:

<%@ OutputCache Duration="120" VaryByParam="selState" %>

This code is sufficient to yield the required results on a Pocket Internet Explorer browser. However, you must also specify the

__EVENTARGUMENT parameter of the

@ OutputCache directive to ensure that this application runs on smaller devices on which the length of the list triggers pagination. We'll explain the reason for this in the next section, "Chapter 11. The DataView object is very useful because you can call the Filter method to create a subset of the data in a DataTable; in this application it selects rows from the master DataTable based on the state code the user enters. The second new feature is the use of the Cache object. This is nothing to do with output caching, but is an object that exposes the ASP.NET Data Cache, which is another optimization tool available to you. We describe the use of the Data Cache later in this chapter.

Listing 13-2: Source for PageCacheByParam.aspx






<%@ OutputCache Duration="10" VaryByParam="selState;__EVENTARGUMENT" %>
<%@ Register TagPrefix="mobile" Namespace="System.Web.UI.MobileControls"
Assembly="System.Web.Mobile" %>
<%@ Page language="c#" Codebehind="PageCacheByParam.aspx.cs"
Inherits="MSPress.MobWeb.PageCacheEx.MobileWebForm"
EnableViewState="false"%>
<mobile:Form id="Form1" runat="server" Paginate="True">
Generated:
<mobile:Label id = "lblTimeMsg" runat="server"/>
<mobile:ObjectList id="oblAuth" runat="server"
AutoGenerateFields="false" LabelField="au_lname"
TableFields="au_lname;au_fname;city;state">
<Field title="Last Name" DataField="au_lname"/>
<Field title="First Name" DataField="au_fname"/>
<Field title="City" DataField="city"/>
<Field title="State" DataField="state"/>
</mobile:ObjectList>
<mobile:Label runat="server" text="Filter by State:"/>
<mobile:SelectionList id="selState" runat="server">
<Item Text="All" Value="*" />
<Item Text="CA" Value="CA" />
<Item Text="IN" Value="IN" />
<Item Text="KS" Value="KS" />
<Item Text="MD" Value="MD" />
<Item Text="MI" Value="MI" />
<Item Text="OR" Value="OR" />
<Item Text="TN" Value="TN" />
<Item Text="UT" Value="UT" />
</mobile:SelectionList>
<mobile:Command runat="server" text="Show" />
</mobile:Form>











Listing 13-3: Code-behind module PageCacheByParam.aspx.cs






using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.UI.MobileControls;
namespace MSPress.MobWeb.PageCacheEx
{
public class MobileWebForm
: System.Web.UI.MobileControls.MobilePage
{
protected System.Web.UI.MobileControls.ObjectList oblAuth;
protected System.Web.UI.MobileControls.SelectionList selState;
protected System.Web.UI.MobileControls.Label lblTimeMsg;
override protected void OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
private void Page_Load(object sender, System.EventArgs e)
{
if (!this.IsPostBack)
{
CacheData();
selState.SelectedIndex = 0;
}
BindtoCachedData();
// Capture the time of the current request.
// Subsequent requests that are cached will show the
// original time.
lblTimeMsg.Text = DateTime.Now.ToString("G");
}
private void CacheData()
{
// Fetch a dataset.
String strConnectionString =
"server=(local)\\NetSDK;database=pubs;Trusted_Connection=yes";
SqlConnection myConnection =
new SqlConnection(strConnectionString);
SqlDataAdapter myCommand =
new SqlDataAdapter("select * from Authors", myConnection);
DataSet ds = new DataSet();
myCommand.Fill(ds, "Authors");
// Save DataSet in the data cache; has application scope.
Cache["DS"] = ds;
}
public void BindtoCachedData()
{
DataSet ds;
try
{
//Get the Dataset from the cache
ds = (DataSet)(Cache["DS"]);
}
catch (NullReferenceException)
{
// Items in the Data Cache may be 'expired' by the system
// so you get a NullreferenceException when you try
// to cast the item. You have to code for this eventuality.
CacheData();
ds = (DataSet)(Cache["DS"]);
}
// Create a DataView based on the filter value.
DataView dv = new DataView(ds.Tables["authors"]);
if (selState.Selection != null)
if (selState.Selection.Value != "*")
dv.RowFilter = "state='" + selState.Selection.Value + "'";
oblAuth.DataSource = dv;
oblAuth.DataBind();
}
}
}











Listing 13-3 retrieves a DataSet object from the database when the user first requests the page. Thereafter, in the BindtoCachedData method, the code builds a DataView object from the DataSet object it retrieves from the Cache object. (The Cache property exposes the data cache, which is described later in this chapter, in the section "Using Data Caching.")


Design Considerations for Applications that Use Caching


You must carefully design applications with which you use caching. You have to consider what will happen if a particular client receives its output from the server cache instead of running code that you've written to execute based on particular events. The PageCacheByParam example shown in Listing 13-3 incorporates solutions for three potential problems that you might encounter when designing applications that use caching.

First of all, you can't use ViewState when using output caching. If the ObjectList control saves its state between every request, the runtime tracks the session through an identifier added to the query string. If the first request fetches its output from the server cache, the runtime never establishes the session, and a runtime error results on a subsequent postback. Consequently, we designed the application in Listing 13-3 to be truly stateless. We included EnableViewState = "false" in the @ Page directive of the mobile Web Forms page, and we designed the application to build a DataView object and bind the ObjectList control to the data on every request.

The second issue in Listing 13-3 is that the application retrieves the data from the database only on the first access of this page, and caches the DataSet object to improve performance on subsequent requests. If the application caches the DataSet object in the Session object, a runtime error would again result when other clients attempt to access this page. If the server cache satisfies the initial page load, the Page_Load method won't execute and the runtime won't store the data for that session. Instead, the application uses the Cache object, which has application scope and is therefore available to all sessions. (We'll describe the Cache object in a moment, in the section "Using Data Caching.")

You'd expect that this application would require you to specify only VaryByParam="selState" to yield the desired result. However, if you've coded the application like this and you run it on a small device such as a WAP-enabled mobile phone, the third problem emerges.

The form in this application specifies Paginate="true". Therefore, the code sends only the first part of the list to the client device and includes the Next and Previous hyperlinks on the page to let the user navigate through the paged list. Each time the user follows one of these links, a postback to the server occurs, fetching the next page to display. An ASP.NET mobile Web application tells the server which page to display by posting back a parameter named __EVENTARGUMENT. Figure 13-2 shows an emulator displaying the Next and Back pagination links.


Figure 13-2: Next and Back pagination links on the Nokia simulator

Consequently, if you don't include this @ OutputCache directive parameter with those you use to determine whether to return a cached copy, when the user selects the Next or Previous link on a page and a cached version is available, the server will simply return the same page. However, specifying the @ OutputCache directive by using VaryByParam="selState;__EVENTARGUMENT" offers a viable solution.


Using Fragment Caching


Fragment caching is a technique you use with user controls; we'll look at user controls in detail in Chapter 20. User controls are a great way of creating reusable graphical components, which implement some piece of user interface functionality that you find yourself using over and over in your applications—for example, in headings, login screens, and user complaints forms(!). You use a user control in much the way as a regular mobile control; you drag it onto a Form and it generates part of the output of the Form, just as if it was a regular control.

You can use fragment caching to cache the output of a user control, which might be only a part of the whole page that is sent to the client. You should use fragment caching where the output of the user control is essentially static and doesn't need to be dynamically generated for each request. Good candidates for fragment caching are navigation links, headers, footers, and other code fragments that can be implemented as user controls and might benefit from the performance gains that are associated with having their output retrieved from a cache. Chapter 20 describes in detail how you can build user controls to encapsulate reusable elements of user interface functionality. You code these controls similarly to the way you code regular mobile Web Forms pages, but you store them in a file with an .ascx extension. You implement fragment caching for a user control by placing an OutputCache directive at the head of the .ascx file, in the same way as we just described for .aspx mobile Web forms pages. This directive applies caching to the output of the user control, independent of the mobile Web Forms page in which you're using the control.

The @ OutputCache directive you use in an .ascx file supports the VaryByParam, VaryByHeader and VaryByCustom attributes, just as when you use it in a .aspx file. However, you can specify one additional attribute in the @ OutputCache directive that applies to user controls: the VaryByControl attribute. This attribute is similar to VaryByParam, but it specifies a particular control within the user control you employ to vary caching. For example, if a user control includes a SelectionList control that has the ID selState, the following would be the @ OutputCache directive that enables caching depending on the value the user selects in the SelectionList:

<%@ OutputCache Duration="320" 
VaryByControl="selState"
VaryByParam="none" %>


Using Data Caching


ASP.NET supports a memory-resident cache that you can use to store arbitrary objects across HTTP requests. We used this cache in Listing 13-3 to store a DataSet object across requests.

Because the data cache has application scope, it's accessible to different sessions. As noted earlier in this chapter, in the section "Design Considerations for Applications that Use Caching," and as implemented by the PageCacheByParam example, this accessibility helps ensure that cached objects remain available to sessions that don't execute setup code because the server cache satisfied their initial request. In Listing 13-3 of the PageCacheByParam example, the code retrieves the DataSet object from the database and stores it in the data cache any time that the Page_Load method executes. This method executes any time the Page cache can't satisfy a particular request for this page. A better solution might be to fetch the DataSet from within the Application_Start method in the Global.asax file.

The data cache is dictionary-structured; therefore, it's very easy to use programmatically:

// Save DataSet in the data cache; has Application scope.
Cache["DS"] = myDataset;


// Get the DataSet from the cache.
DataSet ds = (DataSet)(Cache["DS"]);


The data cache is similar in scope and functionality to the Application object described in Chapter 12. However, the data cache offers this added functionality:



ScavengingBecause the cache is memory resident, it could become full if memory resources become scarce. In this case, ASP.NET removes objects that the runtime hasn't accessed for a while. Code defensively to allow for this. That way, if your application doesn't find an expected object in the cache, it can still access the required object from its original source. See the BindToCachedData method in Listing 13-3, which codes for the eventuality of the requested item not being present in the cache.



Object expirationWhen you add objects to the cache using the Cache.Insert method, you can specify an absolute expiration time for objects in the cache. When the expiration time is reached, ASP.NET removes the object from the data cache. You can also specify a sliding expiration time that's dependent on an object's last use.



File and object dependenciesWhen entering objects into the cache, you can specify the path to a file or to another object. ASP.NET monitors that file or object, and if it's altered, the runtime invalidates any objects in the cache that have a declared dependency on that file or object.



Search the .NET Framework SDK documentation for the Cache.Insert methods and CacheDependency class for further details.

/ 145