User Interface Paging Imagine that you want to use a GridView control to display the results of a database query in multiple pages.. For example, the page in Listing 18.18 uses a GridVie
Trang 1The ObjectDataSource control includes a DataObjectTypeName property This property
contains the name of an object used with the UpdateEmployee() method When the
UpdateEmployee() method is called, an instance of the CompanyEmployee component is
created and passed to the method
NOTE
The DataObjectTypeName property has an effect on only the methods represented by the
InsertMethod, UpdateMethod, and DeleteMethod properties It does not have an effect
on the method represented by the SelectMethod property
There is one important limitation when using the DataObjectTypeName property The
object represented by this property must have a parameterless constructor For example,
you could not use the following CompanyEmployee class with the DataObjectTypeName
property:
public class CompanyEmployee
{
private string _firstName;
public string FirstName
{
get
{
return _firstName;
}
}
public void CompanyEmployee(string firstName)
{
_firstName = firstName;
}
}
The problem with this class is that it initializes its FirstName property in its constructor
Its constructor requires a firstName parameter Instead, you need to use a class that
looks like this:
public class CompanyEmployee
{
private string _firstName;
Trang 2public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
}
}
}
This class has a parameterless constructor The FirstName property is a read/write property
If you have the need, you can get around this limitation by handling the Inserting,
Updating, or Deleting event When you handle one of these events, you can pass any
object that you need to a method These events are discussed later in this chapter in the
section “Handling ObjectDataSource Events.”
Paging, Sorting, and Filtering Data with the
ObjectDataSource Control
The ObjectDataSource control provides you with two options for paging and sorting
data-base data You can take advantage of either user interface or data source paging and
sorting The first option is easy to configure, and the second option has much better
performance In this section, you learn how to take advantage of both options
You also learn how to take advantage of the ObjectDataSource control’s support for
filter-ing When you combine filtering with caching, you can improve the performance of your
data-driven web pages dramatically
User Interface Paging
Imagine that you want to use a GridView control to display the results of a database query
in multiple pages The easiest way to do this is to take advantage of user interface paging
For example, the page in Listing 18.18 uses a GridView and ObjectDataSource control to
display the records from the Movies database table in multiple pages (see Figure 18.4)
Trang 3<%@ Page Language=”C#” %>
<!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 id=”Head1” runat=”server”>
<style type=”text/css”>
.movies td,.movies th
{
padding:5px;
}
</style>
<title>Show User Interface Paging</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
AllowPaging=”true”
PageSize=”3”
Trang 4CssClass=”movies”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”MovieUIPaging”
SelectMethod=”GetMoviesDataSet”
Runat=”server” />
</div>
</form>
</body>
</html>
The GridView control in Listing 18.18 includes an AllowPaging property set to the value
True Setting this property enables user interface paging
The ObjectDataSource control in Listing 18.18 represents the MovieUIPaging component
in Listing 18.19 This component includes a GetMoviesDataSet() method that returns an
ADO.NET DataSet object
To take advantage of user interface paging, you must bind the GridView control to the
right type of data source., which includes a collection, a DataSet, a DataTable, and a
DataView The right type of data source does not include, for example, a DataReader
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
public class MovieUIPaging
{
private readonly string _conString;
public DataSet GetMoviesDataSet()
{
// Create DataAdapter
string commandText = “SELECT Id,Title,Director FROM Movies”;
SqlDataAdapter dad = new SqlDataAdapter(commandText, _conString);
// Return DataSet
DataSet dstMovies = new DataSet();
using (dad)
{
Trang 5dad.Fill(dstMovies);
}
return dstMovies;
}
public MovieUIPaging()
{
_conString =
➥WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString;
}
}
User interface paging is convenient because you can enable it by setting a single property;
however, there is a significant drawback to this type of paging When user interface
paging is enabled, all the movie records must be loaded into server memory If the Movies
database table contains 3 billion records, and you display 3 records a page, all 3 billion
records must be loaded to display the 3 records This places an incredible burden on both
the web server and database server In the next section, you learn how to use data source
paging, which enables you to work efficiently with large sets of records
Data Source Paging
Data source paging enables you to write custom logic for retrieving pages of database
records You can perform the paging in the component, a stored procedure, or a LINQ to
SQL query
If you want the best performance, you should write your paging logic in either a stored
procedure or a LINQ query We examine both approaches in this section
NOTE
Chapter 20 is devoted to the topic of LINQ
The page in Listing 18.20 contains an ObjectDataSource control with data source
paging enabled
<%@ Page Language=”C#” %>
<!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 id=”Head1” runat=”server”>
<style type=”text/css”>
Trang 6.movies td,.movies th
{
padding:5px;
}
</style>
<title>Show Data Source Paging</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
AllowPaging=”true”
PageSize=”3”
CssClass=”movies”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”MoviesDSPaging”
SelectMethod=”GetMovies”
SelectCountMethod=”GetMovieCount”
EnablePaging=”True”
Runat=”server” />
</div>
</form>
</body>
</html>
The ObjectDataSource control includes an EnablePaging property that has the value True
The ObjectDataSource also includes a SelectCountMethod property that represents the
name of a method that retrieves a record count from the data source
Furthermore, that the GridView control includes both an AllowPaging and PageSize
prop-erty Even when using data source paging, you need to enable the AllowPaging property
for the GridView so that the GridView can render its paging user interface
When an ObjectDataSource control has its EnablePaging property set to the value True,
the ObjectDataSource passes additional parameters when calling the method represented
by its SelectMethod property The two additional parameters are named StartRowIndex
and MaximumRows
Trang 7Now that we have the page setup for data source paging, we need to create the
compo-nent Let’s start by using a LINQ to SQL query This approach is the easiest and
recom-mended way The component in Listing 18.21 uses LINQ to SQL queries to implement
both the GetMovies() and GetMovieCount() methods
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Web;
public class MoviesDSPaging
{
public static IEnumerable<Movie> GetMovies(int startRowIndex, int maximumRows)
{
MyDatabaseDataContext db = new MyDatabaseDataContext();
return db.Movies.Skip(startRowIndex).Take(maximumRows);
}
public static int GetMovieCount()
{
HttpContext context = HttpContext.Current;
if (context.Cache[“MovieCount”] == null)
context.Cache[“MovieCount”] = GetMovieCountFromDB();
return (int)context.Cache[“MovieCount”];
}
private static int GetMovieCountFromDB()
{
MyDatabaseDataContext db = new MyDatabaseDataContext();
return db.Movies.Count();
}
}
Before you can use the component in Listing 18.21, you need to create a DataContext
named MyDatabaseDataContext You can create this DataContext by selecting Website,
Add New Item, and adding a new LINQ to SQL Classes item to your website Name the
new LINQ to SQL Classes item MyDatabase.dbml Next, after the LINQ to SQL Designer
opens, drag the Movies database table from the Database Explorer window onto the
Designer surface
Trang 8NOTE
Unfortunately, when you drag the Movies database table onto the LINQ to SQL Designer
surface, the Designer may create a new entity named Movy The Designer is attempting
to singularize the word and it fails badly You must rename the entity to Movie in the
Properties window
You are not required to use LINQ to SQL when you want to implement data source
paging As an alternative to LINQ to SQL, you can perform your paging logic within a SQL
stored procedure The component in Listing 18.22 contains ADO.NET code instead of
LINQ to SQL queries
using System;
using System.Web;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
public class MoviesDSPaging
{
private static readonly string _conString;
public static SqlDataReader GetMovies(int startRowIndex, int maximumRows)
{
// Initialize connection
SqlConnection con = new SqlConnection(_conString);
// Initialize command
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = “GetPagedMovies”;
cmd.CommandType = CommandType.StoredProcedure;
// Add ADO.NET parameters
cmd.Parameters.AddWithValue(“@StartRowIndex”, startRowIndex);
cmd.Parameters.AddWithValue(“@MaximumRows”, maximumRows);
// Execute command
con.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
Trang 9public static int GetMovieCount()
{
HttpContext context = HttpContext.Current;
if (context.Cache[“MovieCount”] == null)
context.Cache[“MovieCount”] = GetMovieCountFromDB();
return (int)context.Cache[“MovieCount”];
}
private static int GetMovieCountFromDB()
{
int result = 0;
// Initialize connection
SqlConnection con = new SqlConnection(_conString);
// Initialize command
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = “SELECT Count(*) FROM Movies”;
// Execute command
using (con)
{
con.Open();
result = (int)cmd.ExecuteScalar();
}
return result;
}
static MoviesDSPaging()
{
_conString =
➥WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString;
}
}
To improve performance, the GetMovieCount() method attempts to retrieve the total
count of movie records from the server cache If the record count cannot be retrieved
from the cache, the count is retrieved from the database
The GetMovies() method calls a stored procedure named GetPagedMovies to retrieve a
particular page of movies The StartRowIndex and MaximumRows parameters are passed to
the stored procedure The GetPagedMovies stored procedure is contained in Listing 18.23
Trang 10CREATE PROCEDURE dbo.GetPagedMovies
(
@StartRowIndex INT,
@MaximumRows INT
)
AS
Create a temp table to store the select results
CREATE TABLE #PageIndex
(
IndexId INT IDENTITY (1, 1) NOT NULL,
RecordId INT
)
INSERT into the temp table
INSERT INTO #PageIndex (RecordId)
SELECT Id FROM Movies
Get a page of movies
SELECT
Id,
Title,
Director,
DateReleased
FROM
Movies
INNER JOIN #PageIndex WITH (nolock)
ON Movies.Id = #PageIndex.RecordId
WHERE
#PageIndex.IndexID > @startRowIndex
AND #PageIndex.IndexID < (@startRowIndex + @maximumRows + 1)
ORDER BY
#PageIndex.IndexID
The GetPagedMovies stored procedure returns a particular page of database records The
stored procedure creates a temporary table named #PageIndex that contains two columns:
an identity column and a column that contains the primary key values from the Movies
database table The temporary table fills in any holes in the primary key column that
might result from deleting records
Next, the stored procedure retrieves a certain range of records from the #PageIndex table
and joins the results with the Movies database table The end result is that only a single
page of database records is returned