ptg 794 CHAPTER 18 Using the ObjectDataSource Control The 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; From the Library of Wow! eBook ptg 795 Paging, Sorting, and Filtering Data with the ObjectDataSource Control 18 public 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). From the Library of Wow! eBook ptg 796 CHAPTER 18 Using the ObjectDataSource Control LISTING 18.18 ShowUIPaging.aspx <%@ 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” FIGURE 18.4 Displaying multiple pages with user interface paging. From the Library of Wow! eBook ptg 797 Paging, Sorting, and Filtering Data with the ObjectDataSource Control 18 CssClass=”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. LISTING 18.19 MovieUIPaging.cs 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) { From the Library of Wow! eBook ptg 798 CHAPTER 18 Using the ObjectDataSource Control dad.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. LISTING 18.20 ShowDSPaging.aspx <%@ 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”> From the Library of Wow! eBook ptg 799 Paging, Sorting, and Filtering Data with the ObjectDataSource Control 18 .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. From the Library of Wow! eBook ptg 800 CHAPTER 18 Using the ObjectDataSource Control Now 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. LISTING 18.21 MoviesLINQPaging.cs 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. From the Library of Wow! eBook ptg 801 Paging, Sorting, and Filtering Data with the ObjectDataSource Control 18 NOTE 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. LISTING 18.22 MoviesSQLPaging.cs 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); } From the Library of Wow! eBook ptg 802 CHAPTER 18 Using the ObjectDataSource Control 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() { 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. From the Library of Wow! eBook ptg 803 Paging, Sorting, and Filtering Data with the ObjectDataSource Control 18 LISTING 18.23 GetPagedMovies.sql CREATE 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. From the Library of Wow! eBook . 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. 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. the DataObjectTypeName property: public class CompanyEmployee { private string _firstName; public string FirstName { get { return _firstName; } } public void CompanyEmployee(string firstName)