Click to Rate and Give Feedback
Related Articles
Here the author introduces SQL Server Data Services, which exposes its functionality over standard Web service interfaces.

By David Robinson (July 2008)
Here the author answers questions regarding the Entity Framework and provides an understanding of how and why it was developed.

By Elisa Flasko (July 2008)
Here we present techniques for programmatic and declarative data binding and display with Windows Presentation Foundation.

By Josh Smith (July 2008)
Systems that handle failure without losing data are elusive. Learn how to achieve systems that are both scalable and robust.

By Udi Dahan (July 2008)
More ...
Articles by this Author
Storing data securely in a configuration system is not an easy problem to solve. While I was on the ASP. NET team, this particular feature, secure connection string storage, looked as if it wouldn’t get done.

By Rob Howard (May 2006)
In my childhood I spent several weeks a year in Holland with my extended family. As a young American boy I was fascinated with the electric Dutch trains, something we didn't see in my hometown of Dallas, Texas.

By Rob Howard (October 2005)
In my last column, I discussed Microsoft® Application Center Test and how it could be used to measure the performance of your Web application (see Extreme ASP. NET: Tools of the Trade: Application Center Test).

By Rob Howard (August 2005)
When you sit down to write an ASP. NET application, how much time do you spend thinking about performance? It's unfortunate, but for most developers performance is an afterthought. Performance planning and design really need to be front and center.

By Rob Howard (June 2005)
Writing a Web application with ASP.NET is unbelievably easy. So many developers don't take the time to structure their applications for great performance. In this article, the author presents 10 tips for writing high-performance Web apps. The discussion is not limited to ASP.NET applications because they are just one subset of Web applications.

By Rob Howard (January 2005)
More ...
Popular Articles
See how to build a document-level Visual Studio Tools for Office customization and integrate it with a content type in SharePoint.

By Steve Fox (May 2008)
Here we introduce you to some of the concepts behind the new F# language, which combines elements of functional and object-oriented .NET languages. We then help you get started writing some simple programs.

By Ted Neward (Launch 2008)
Here we present techniques for programmatic and declarative data binding and display with Windows Presentation Foundation.

By Josh Smith (July 2008)
Here the author answers questions regarding the Entity Framework and provides an understanding of how and why it was developed.

By Elisa Flasko (July 2008)
More ...
Read the Blog
Windows Presentation Foundation (WPF) offers excellent support for managing the display and editing of complex data. In the December 2007 edition of MSDN Magazine, John Papa did a great job of explaining essential WPF data binding concepts. ...
Read more!
The most fundamental form of Web testing is HTTP request/response testing. This involves programmatically sending an HTTP request to the Web application, fetching the HTTP response, and examining the response for an expected value. In the May 2008 issue of MSDN Magazine, Read more!
In the November issue of MSDN Magazine, Jeffrey Richter demonstrates some recent additions to the C# programming language that make working with the APM significantly easier. In the June ...
Read more!
The July 2008 issue of MSDN Magazine is now available online. Here's what's in the issue: Data Services: Develop ...
Read more!
The June 2008 issue features the first installment of a new MSDN Magazine column on software design fundamentals. We’ll discuss design patterns and principles in a manner that isn't bound to a specific tool or lifecycle methodology. In this issue, Jeremy Miller starts the Patterns in Practice column ...
Read more!
In the April 2008 issue of MSDN Magazine, Kenny Kerr introduced the Windows Imaging Component (WIC), showing you how you can use it to encode and decode different image ...
Read more!
More ...
Extreme ASP.NET
A Little Bit of Control for Your Controls
Rob Howard

Having worked for so many years designing and developing ASP.NET while at Microsoft, it's exciting now to have a venue in which to talk about it. In this new column, Extreme ASP.NET, I'll discuss and demonstrate time-tested techniques and approaches to implementing high-performance, reliable, secure, and user-friendly Web applications with ASP.NET.

Server Controls
When you're first learning to use ASP.NET, you'll quickly discover that there are various layers to the technology. The HttpRuntime is the lowest layer and has many similarities with the IIS ISAPI programming model. Using the HttpRuntime, you could actually build many technologies such as ASP, PHP, Cold Fusion, and so on.
The HttpRuntime is simply a request/response processing engine. The ASP.NET page model is then an implementation on top of the HttpRuntime—something most developers are more familiar with. Within the ASP.NET page model, you will then quickly find yourself using server controls to compose rich and complex page functionality using simple declarative statements.
ASP.NET controls, better known as server controls because they run on the server, are the developer's toolset for quickly writing Web applications. In fact, server controls are what ASP.NET page development is all about. Using them you can easily get great functionality for database access, calendars, textboxes, dropdown lists, and lots of other common composite Web functionality. Plus you can do it quickly through declarative programming.
The sooner you realize just how essential controls are to the ASP.NET page programming model, the better. For example, in ASP.NET 2.0 there are nearly 60 new server controls. The fact that the Page class is also a control indicates just how important server controls are to ASP.NET.
Both personally and professionally, I'm an advocate of writing as little code in a page as possible by encapsulating user interface functionality into server controls. If you download and examine Community Server (www.communityserver.org) you'll find that 99 percent of the pages are containers for server controls. All UI behavior, layout, and other aspects of the interface are accessed strictly through server controls.
In the January 2005 issue of MSDN®Magazine, I discussed 10 performance tips for writing better ASP.NET applications (see ASP.NET: 10 Tips for Writing High-Performance Web Applications). One of those tips was to reduce the amount of data sent back and forth from the database while another was to batch multiple resultsets together to reduce the number of round-trips. In this month's column I'll look at combining those tips and exposing their functionality through a custom server control that handles paging.

The Need for Data Paging
There are two ways you can create a server control. One way is to take any ASP.NET page, make minor modifications to the code, and rename the extension from aspx to ascx. The user control can then be used declaratively within other pages. The second way is to crack open your favorite code editor and write a class that ultimately derives from the Control base class.
Writing compiled server controls is my preference. Compiled controls give you the greatest flexibility over what your control does. I can't possibly go into all the details and nuances of compiled server controls—for that I'd recommend you pick up the excellent book Developing Microsoft ASP.NET Server Controls and Components by Nikhil Kothari and Vandana Datye (Microsoft Press®, 2002).
One of the tips in my article concerned reducing time spent communicating with the database or "time spent on the wire." The most salient point this tip makes is that going back and forth to the database repeatedly is inefficient. Instead, a more efficient technique is to return multiple resultsets from the database when possible. This is accomplished using a stored procedure, and each resultset is processed using the DataReader's NextResult method:
// read first resultset
reader = command.ExecuteReader();

Supplier.DataSource = reader;
Supplier.DataBind();

// read next resultset
reader.NextResult();

Products.DataSource = reader;
Products.DataBind();
The second tip, which also used the multiple resultset tip, is to be smart about how much data to return from the database. Using a technique known as paging, resultsets are further reduced to only the data required to satisfy the request. However, to successfully page through the data you also have to know how many records are returned in the original query, thus the need to return a second resultset containing the total number of records.
Figure 1 shows a stored procedure that can be used to page through the Orders table in the sample Northwind database. The procedure first calculates the total number of records it has to return to retrieve the requested data. It sets the ROWCOUNT to this value, forcing SQL Server to return only this specified number of records. It then does some calculations on lower and upper bounds of the page. Next a temporary #PageIndex table is created with an IndexID identity column and an OrderID column. Note that if I were working with extremely large sets of data I could make some additional optimizations, such as adding a SQL index on the IndexID column.
Next I insert into the temporary #PageIndex table the OrderID values from the Orders table. If I wanted to select only data that matched a specific criteria, I would add my WHERE clause, like I did here to get a specific data range. The number of records added to the #PageIndex table is limited based on the ROWCOUNT set previously. I then retrieve the total number of records available with SELECT COUNT(OrderID) FROM Orders. This is my first resultset returned from the stored procedure.
Finally, I SELECT the data I want from the Orders table by joining on the temporary #PageIndex table and controlling the returned results through the desired upper and lower bounds requests and ordering by the IndexID.
You might be wondering why I don't just use the OrderID or another identity column on the Orders table for accomplishing page selection. Well I can't do this for several reasons: first, there is no guarantee that the initial values in the identity column are contiguous (a row might have been deleted), and second, the numerical order will not be contiguous if a WHERE clause is used. The #PageIndex table builds a contiguous set of numerically ordered values based on how they were selected from the Orders table.
As you can see, this complicates the data access layer quite a bit, although it makes data access more efficient as the database grows. Requests execute through a stored procedure that takes a number of parameters, such as the page index, page number, and any other information required to create the resultset. Once returned, the resultset must be processed and data returned.
So you get the general idea—lots of work to get the desired result!
One thing you can do to simplify this data access functionality is create a server control to expose the UI required to page through data. Such a control would allow you to easily navigate to the first or last record with a single click or to walk through pages one at a time by clicking. The following shows the kind of UI I would expect to see:
Page 1 of 19 (373 items) 1 2 3 4 5 >> ... Last >>
There are two server controls used to generate the UI. The first is a server control called CurrentPage and the second is a control called Pager. Both are custom controls that ship with Community Server, the full source of which is available with the released version. Here I'm going to examine only the Pager control.

Using the Pager Control
The Pager control performs the following actions: it generates UI for easy navigation to the first or last page with a single click, it walks backwards and forwards through pages using numerical page numbers or forward/backward links, it displays the current page as plain text (not a link), and it raises a server-side event when a link button on the pager is clicked. Figure 2 shows a sample use of the Pager control used with a DataGrid.
Let's take a look at the Page_Load method and the event raised by the Pager control in this page—remember, ideally you would encapsulate all of this functionality in a separate control too, but for the purpose of this column I'll keep it simple:
public void Page_Load() {
  if (!Page.IsPostBack)
    Bind();
}
In the Page_Load I determine if I've already bound the data. If it's the first request, I'll call Bind to initially bind the DataGrid with default data and also set up the Pager (see Figure 3).
In the Bind procedure I create a connection to the database and attempt to execute a stored procedure. This particular procedure, as you saw in Figure 3, requires that the page index and the page size be set. These settings tell the procedure which page you want returned and how large each page is.
Next, I open the connection to the database. The first resultset returned tells me how many total records are available that meet the criteria of the clauses in the stored procedure query. In Figure 2 the procedure is relatively simple; a more complex procedure might filter data, sort, or order the resultset through other parameters—this is exactly what my team did for the Forums feature of Community Server.
After I call Bind, the DataGrid and the Pager are both bound and rendered. When the user of the paged grid decides to click on the Pager control to navigate forwards (or backwards) the following event is raised on the server:
void Index_Changed (sender As Object, e As EventArgs) {
    Bind();
}
This calls the Bind method and rebinds the DataGrid using the PageIndex value that the user clicked on or selected from the Pager.
You might be wondering why I'm going through all of this complicated paging functionality when the DataGrid already has built-in paging. What you probably didn't know is that there is a small problem with the way paging works in the DataGrid. Let's say your data layer returns 1,000 records and you enable DataGrid paging with a page size of 25—meaning 25 records displayed at a time. The DataGrid accomplishes this by binding all 1,000 records and then discarding 975 of them. So, as you page through the data you'll first show records 1-25 discarding 26-1,000. When you click Next, records 26-50 would be shown while records 1-25 and 51-1,000 would be discarded. As long as the number of records returned is relatively small this is a fairly insignificant problem.
However, as the resultsets grow, the application will experience more and more serious performance problems because it has to spend more and more time communicating with the database—imagine the earlier case in which five million records are returned! Furthermore, the whole point of paging data is to be able to deal with so much returned data. The Pager control isn't necessary for all data access, but for paging through large sets of data it will make your data access more efficient.
Note that ASP.NET 2.0 provides support for data source-level paging. Using ObjectDataSource, you can write a SelectMethod that accepts parameters for a starting row index and the maximum number of rows to return. If you then enable paging in a connected DataGridView (not a DataGrid), the DataGridView will take advantage of the paging support in the data source. You can also configure a separate method (SelectCountMethod) to return the total number of rows in the resultset (regardless of the page size) so that DataGridView can use this number to calculate how to render pager UI links. In this case, the DataGridView asks the data source for just the data it needs in order to render the current page instead of asking for all of the data and throwing away rows.

Implementing the Pager Control
Now that you've seen how the Pager control works, let's look at its implementation. The Pager control derives from the ASP.NET Label control and uses the INamingContainer marker interface:
public class Pager : Label, INamingContainer {
As shown earlier, there are a couple of properties on the Pager that must be set. The first is the TotalRecords property. This property tells the control how many total records exist in the data source with which the Pager is associated. The second is PageSize. In the control there is a default value of 25. PageSize is required to calculate the total number of pages available in the CalculateTotalPages helper function (see Figure 4).
Next, let's look at how the control renders itself. The Pager overrides both the CreateChildControls and Render methods. All of the event wire-up happens in CreateChildControls when the control tree is created. To ensure that the event is wired correctly, a link button is created for every page available. Later the Render method is called when it is time for the Pager to render its output. Within Render there exists logic to display the correct link buttons based on the current PageIndex and PageSize being used. For example, the RenderLast method is used to Render a link button with the text "Last", but only when the last page is not the current page:
void RenderLast (HtmlTextWriter writer) {

    int totalPages = CalculateTotalPages();

    if (((PageIndex + 3) < totalPages) && (totalPages > 5)) {
        LiteralControl l = new LiteralControl("&nbsp;...&nbsp;");
        l.RenderControl(writer);

        lastButton.RenderControl(writer);
    }

}
The most complex logic is in the RenderPagingButtons method (see Figure 5).
As shown earlier, applications that use the Pager control must subscribe to an event raised by the Pager when a new page index is selected by the user:
public event System.EventHandler IndexChanged;
All the LinkButtons added to the PagerControl are wired to an internal PageIndex_Click event handler. Two things happen when an internal click event is raised. First, the PageIndex is set to the page selected by the user. The page index is stored as a CommandArgument of each LinkButton. Second, the IndexChanged event is raised and subscribers can handle the event. Here is the code that handles the internal PageIndex_Click event:
void PageIndex_Click(Object sender, EventArgs e) {

    PageIndex = Convert.ToInt32(((LinkButton) sender).CommandArgument);

    if (null != IndexChanged)
        IndexChanged(sender, e);

}
The biggest shortcoming of the Pager control—and something I'll likely fix in a future version of the Community Server controls—is that it requires postbacks. Due to some logic paths, postbacks are required, and while it's technically possible for a search engine to follow postback links, Google, MSN® Search, and other popular search engines don't currently do so. In a future version we'll likely support standard links in the pager, too.
Using the Pager control combined with some smarter data access tips can help your ASP.NET application be more efficient when using the database. The Pager server control shows how you can encapsulate some complex functionality into a reusable server control that supports events. Controls make writing reusable functionality incredibly easy in ASP.NET.

Send your questions and comments for Rob to  xtrmasp@microsoft.com.


Rob Howard is a founder of Telligent Systems, specializing in high-performance Web apps and knowledge management and collaboration systems. Previously, Rob was employed by Microsoft where he helped design the infrastructure features of ASP.NET 1.0, 1.1, and 2.0.

Page view tracker