ptg 894 CHAPTER 19 Building Data Access Components with ADO.NET void Page_Load() { // Create connection string connectionString = WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString; SqlConnection con = new SqlConnection(connectionString); // Create Select command dad = new SqlDataAdapter(“SELECT Id,Title,Director FROM Movies”, con); // Create Update, Insert, and Delete commands with SqlCommandBuilder SqlCommandBuilder builder = new SqlCommandBuilder(dad); // Add data to DataTable dtblMovies = new DataTable(); dad.Fill(dtblMovies); // Bind data to Repeater rptMovies.DataSource = dtblMovies; rptMovies.DataBind(); } protected void lnkUpdate_Click(object sender, EventArgs e) { // Update DataTable with changes for (int i=0; i < rptMovies.Items.Count;i++) { RepeaterItem item = rptMovies.Items[i]; TextBox txtTitle = (TextBox)item.FindControl(“txtTitle”); TextBox txtDirector = (TextBox)item.FindControl(“txtDirector”); if (dtblMovies.Rows[i][“Title”] != txtTitle.Text) dtblMovies.Rows[i][“Title”] = txtTitle.Text; if (dtblMovies.Rows[i][“Director”] != txtDirector.Text) dtblMovies.Rows[i][“Director”] = txtDirector.Text; } // Set batch size to maximum size dad.UpdateBatchSize = 0; // Perform update int numUpdated = dad.Update(dtblMovies); lblResults.Text = String.Format(“Updated {0} rows”, numUpdated); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> From the Library of Wow! eBook ptg 895 Disconnected Data Access 19 <title>Show DataAdapter Update</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:Repeater id=”rptMovies” EnableViewState=”false” Runat=”server”> <HeaderTemplate> <table> <tr> <th>Title</th><th>Director</th> </tr> </HeaderTemplate> <ItemTemplate> <tr> <td> <asp:TextBox id=”txtTitle” Text=’<%#Eval(“Title”)%>’ Runat=”server” /> </td> <td> <asp:TextBox id=”txtDirector” Text=’<%#Eval(“Director”)%>’ Runat=”server” /> </td> </tr> </ItemTemplate> <FooterTemplate> </table> </FooterTemplate> </asp:Repeater> <br /> <asp:LinkButton id=”lnkUpdate” Text=”Update Movies” Runat=”server” OnClick=”lnkUpdate_Click” /> <br /><br /> From the Library of Wow! eBook ptg 896 CHAPTER 19 Building Data Access Components with ADO.NET <asp:Label id=”lblResults” EnableViewState=”false” Runat=”server” /> </div> </form> </body> </html> The SqlDataAdapter in Listing 19.27 performs a batch update. When a SqlDataAdapter object’s UpdateBatchSize property is set to the value 0, the SqlDataAdapter performs all its updates in a single batch. If you want to perform updates in smaller batches, you can set the UpdateBatchSize to a particular size. Using the DataTable Object The DataTable object represents an in-memory database table. You can add rows to a DataTable with a SqlDataAdapter, with a SqlDataReader, with an XML file, or program- matically. For example, the page in Listing 19.28 builds a new DataTable programmati- cally. The contents of the DataTable then display in a GridView control (see Figure 19.14). FIGURE 19.14 Displaying a DataTable that was built programmatically. From the Library of Wow! eBook ptg 897 Disconnected Data Access 19 LISTING 19.28 ShowDataTableProgram.aspx <%@ Page Language=”C#” %> <%@ Import Namespace=”System.Data” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> void Page_Load() { // Create the DataTable columns DataTable newDataTable = new DataTable(); newDataTable.Columns.Add(“Id”, typeof(int)); newDataTable.Columns.Add(“ProductName”, typeof(string)); newDataTable.Columns.Add(“ProductPrice”, typeof(decimal)); // Mark the Id column as an autoincrement column newDataTable.Columns[“Id”].AutoIncrement = true; // Add some data rows for (int i = 1; i < 11; i++) { DataRow newRow = newDataTable.NewRow(); newRow[“ProductName”] = “Product “ + i.ToString(); newRow[“ProductPrice”] = 12.34m; newDataTable.Rows.Add(newRow); } // Bind DataTable to GridView grdProducts.DataSource = newDataTable; grdProducts.DataBind(); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Show DataTable Programmatically</title> </head> <body> <form id=”form1” runat=”server”> <div> <h1>Products</h1> From the Library of Wow! eBook ptg 898 CHAPTER 19 Building Data Access Components with ADO.NET <asp:GridView id=”grdProducts” Runat=”server” /> </div> </form> </body> </html> In Listing 19.28, a DataTable with the following three columns is created: Id, ProductName, and ProductPrice. The data type of each column is specified with a .NET Framework type. For example, the ProductPrice column is created as a decimal column. Alternatively, you could create each column with a SqlType. For example, you could use System.Data.SqlTypes.SqlDecimal for the type of the ProductPrice column. The Id column is created as an autoincrement column. When you add new rows to the DataTable, the column increments its value automatically. Selecting DataRows You can retrieve particular rows from a DataTable by using the DataTable object’s Select() method. The Select() method accepts a filter parameter. You can use just about anything that you would use in a SQL WHERE clause with the filter parameter. When you retrieve an array of rows with the Select() method, you can also specify a sort order for the rows. When specifying a sort order, you can use any expression that you would use with a SQL ORDER BY clause. For example, the page in Listing 19.29 caches a DataTable in memory with the ASP.NET Cache object. The page contains a TextBox control. When you enter a partial movie title into the TextBox control, a list of matching movies is displayed in a GridView control. The rows are sorted in order of the movie title (see Figure 19.15). From the Library of Wow! eBook ptg 899 Disconnected Data Access 19 LISTING 19.29 ShowDataTableSelect.aspx <%@ Page Language=”C#” %> <%@ Import Namespace=”System.Data” %> <%@ Import Namespace=”System.Data.SqlClient” %> <%@ Import Namespace=”System.Web.Configuration” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> protected void btnSearch_Click(object sender, EventArgs e) { // Get movies DataTable from Cache DataTable dtblMovies = (DataTable)Cache[“MoviesToFilter”]; if (dtblMovies == null) { dtblMovies = GetMoviesFromDB(); Cache[“MoviesToFilter”] = dtblMovies; } // Select matching rows string filter = String.Format(“Title LIKE ‘{0}*’”, txtTitle.Text); DataRow[] rows = dtblMovies.Select(filter, “Title”); FIGURE 19.15 Selecting matching rows from a cached DataTable. From the Library of Wow! eBook ptg 900 CHAPTER 19 Building Data Access Components with ADO.NET // Bind to GridView grdMovies.DataSource = rows; grdMovies.DataBind(); } private DataTable GetMoviesFromDB() { string connectionString = WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString; SqlDataAdapter dad = new SqlDataAdapter( “SELECT Title, Director FROM Movies”, connectionString); DataTable dtblMovies = new DataTable(); dad.Fill(dtblMovies); return dtblMovies; } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <style type=”text/css”> th, td { padding:5px; } </style> <title>Show DataTable Select</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:TextBox id=”txtTitle” Tooltip=”Search” Runat=”server” /> <asp:Button id=”btnSearch” Text=”Search” Runat=”server” OnClick=”btnSearch_Click” /> <hr /> <asp:GridView id=”grdMovies” AutoGenerateColumns=”false” From the Library of Wow! eBook ptg 901 Disconnected Data Access 19 Runat=”server”> <Columns> <asp:TemplateField HeaderText=”Title”> <ItemTemplate> <%# ((DataRow)Container.DataItem)[“Title”] %> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText=”Director”> <ItemTemplate> <%# ((DataRow)Container.DataItem)[“Director”] %> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> </div> </form> </body> </html> The DataTable Select() method returns an array of DataRow objects. There is nothing wrong with binding an array of DataRow objects to a GridView control. However, you must explicitly cast each data item to a DataRow and read within a GridView TemplateField. DataRow States and DataRow Versions When you modify the rows in a DataTable, the DataTable keeps track of the changes that you make. A DataTable maintains both the original and modified version of each row. Each row in a DataTable has a particular RowState that has one of the following values: . Unchanged—The row has not been changed. . Added—The row has been added. . Modified—The row has been modified. . Deleted—The row has been deleted. . Detached—The row has been created but not added to the DataTable. Each row in a DataTable can have more than one version. Each version is represented by one of the following values of the DataRowVersion enumeration: . Current—The current version of the row. . Default—The default version of the row. . Original—The original version of the row. . Proposed—The version of a row that exists during editing. From the Library of Wow! eBook ptg 902 CHAPTER 19 Building Data Access Components with ADO.NET You can use the DataTable.AcceptChanges() method to copy the current versions of all the rows to the original versions of all the rows. And you can use the DataTable.RejectChanges() method to copy the original versions of all the rows to the current versions of all the rows. For example, the component in Listing 19.30 includes an AcceptChanges() and RejectChanges() method. The component maintains a DataTable in Session state. If you update a row in the DataTable, the row is updated in memory. If the RejectChanges() method is called, any changes made to the DataTable are rejected. If the AcceptChanges() method is called, the database is updated and all changes are accepted. LISTING 19.30 App_Code\Movie9.cs using System; using System.Data; using System.Data.SqlClient; using System.Web; using System.Web.Configuration; public class Movie9 { private SqlDataAdapter dad = new SqlDataAdapter(); public DataTable GetAll() { return (DataTable)HttpContext.Current.Session[“MoviesToEdit”]; } public void Update(int id, string title, string director) { DataTable movies = (DataTable)HttpContext.Current.Session[“MoviestoEdit”]; DataRow rowToEdit = movies.Rows.Find(id); rowToEdit[“title”] = title; rowToEdit[“director”] = director; } public void RejectChanges() { DataTable movies = (DataTable)HttpContext.Current.Session[“MoviestoEdit”]; movies.RejectChanges(); } From the Library of Wow! eBook ptg 903 Disconnected Data Access 19 public void AcceptChanges() { DataTable movies = (DataTable)HttpContext.Current.Session[“MoviestoEdit”]; dad.Update(movies); movies.AcceptChanges(); } public Movie9() { // Create Data Adapter string connectionString = WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString; dad = new SqlDataAdapter( “SELECT Id,Title,Director FROM Movies”, connectionString); SqlCommandBuilder builder = new SqlCommandBuilder(dad); dad.UpdateBatchSize = 0; HttpContext context = HttpContext.Current; if (context.Session[“MoviesToEdit”] == null) { // Add data to DataTable DataTable dtblMovies = new DataTable(); dad.Fill(dtblMovies); dtblMovies.PrimaryKey = new DataColumn[] { dtblMovies.Columns[“Id”] }; context.Session[“MoviesToEdit”] = dtblMovies; } } } The page in Listing 19.31 contains a GridView that is bound to the component in Listing 19.30. The GridView includes a column that indicates whether each row has been changed. The column displays the value of the corresponding DataRow object’s RowState property (see Figure 19.16). From the Library of Wow! eBook . </tr> </ItemTemplate> <FooterTemplate> </table> </FooterTemplate> < /asp: Repeater> <br /> < ;asp: LinkButton id=”lnkUpdate” Text=”Update Movies” Runat=”server”. </html> The SqlDataAdapter in Listing 19.27 performs a batch update. When a SqlDataAdapter object’s UpdateBatchSize property is set to the value 0, the SqlDataAdapter performs all its updates in a single. following three columns is created: Id, ProductName, and ProductPrice. The data type of each column is specified with a .NET Framework type. For example, the ProductPrice column is created as a decimal