Using the DataView Object The DataView object represents an in-memory database view.. You also can directly instantiate a new DataView object by passing a DataTable, filter, sort order,
Trang 1LISTING 19.31 ShowMovie9.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”>
protected void btnReject_Click(object sender, EventArgs e)
{
Movie9 movie = new Movie9();
movie.RejectChanges();
grdMovies.DataBind();
}
protected void btnAccept_Click(object sender, EventArgs e)
{
Movie9 movie = new Movie9();
movie.AcceptChanges();
grdMovies.DataBind();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
FIGURE 19.16 Tracking data row changes
Trang 2<head id=”Head1” runat=”server”>
<title>Show Movie9</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<h1>Edit Movies</h1>
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
DataKeyNames=”Id”
AutoGenerateEditButton=”true”
Runat=”server”>
<Columns>
<asp:TemplateField>
<ItemTemplate>
<%# ((DataRowView)Container.DataItem).Row.RowState %>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<br />
<asp:Button
id=”btnReject”
Text=”Reject Changes”
OnClick=”btnReject_Click”
Runat=”server” />
<asp:Button
id=”btnAccept”
Text=”Accept Changes”
OnClick=”btnAccept_Click”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie9”
SelectMethod=”GetAll”
UpdateMethod=”Update”
Runat=”server” />
</div>
Trang 3</form>
</body>
</html>
If you click the Accept Changes button, all the changes made to the rows in the GridView
are sent to the database If you click the Reject Changes button, all the rows revert to their
original values
Using the DataView Object
The DataView object represents an in-memory database view You can use a DataView
object to create a sortable, filterable view of a DataTable
The DataView object supports three important properties:
Sort—Enables you to sort the rows represented by the DataView
RowFilter—Enables you to filter the rows represented by the DataView
RowStateFilter—Enables you to filter the rows represented by the DataView
accord-ing to the row state (for example, OriginalRows, CurrentRows, Unchanged)
The easiest way to create a new DataView is to use the DefaultView property exposed by
the DataTable class like this:
Dim dataView1 As DataView = dataTable1.DefaultView;
The DefaultView property returns an unsorted, unfiltered view of the data contained in
a DataTable
You also can directly instantiate a new DataView object by passing a DataTable, filter, sort
order, and DataViewRowState filter to the DataView object’s constructor, like this:
DataView dataView1 = new DataView(dataTable1,
“BoxOfficeTotals > 100000”,
“Title ASC”,
DataViewRowState.CurrentRows);
This statement creates a new DataView from a DataTable that represents the Movies
data-base table The rows are filtered to include only the movies that have a box office total
greater than 100,000 dollars Also, the rows are sorted by the movie title in ascending
order Finally, all the current rows are represented from the DataTable (as opposed, for
instance, to rows that have been deleted)
The page in Listing 19.30 illustrates one way that you can use a DataView In Listing
19.32, a DataView is cached in Session state You can sort the cached DataView by clicking
on the header links rendered by the GridView control (see Figure 19.17)
Trang 4LISTING 19.32 ShowDataView.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”>
void Page_Load()
{
if (Session[“MoviesToSort”] == null)
{
string connectionString =
WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString;
SqlDataAdapter dad = new SqlDataAdapter(
“SELECT Id,Title,Director FROM Movies”, connectionString);
DataTable dtblMovies = new DataTable();
dad.Fill(dtblMovies);
Session[“MoviesToSort”] = dtblMovies.DefaultView;
}
if (!Page.IsPostBack)
FIGURE 19.17 Sorting a cached DataView
Trang 5BindMovies();
}
void BindMovies()
{
grdMovies.DataSource = Session[“MoviesToSort”];
grdMovies.DataBind();
}
protected void grdMovies_Sorting(object sender, GridViewSortEventArgs e)
{
DataView dvwMovies = (DataView)Session[“MoviesToSort”];
dvwMovies.Sort = e.SortExpression;
BindMovies();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show DataView</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
AllowSorting=”true”
OnSorting=”grdMovies_Sorting”
Runat=”server” />
</div>
</form>
</body>
</html>
Using the DataSet Object
The DataSet object represents an in-memory database A single DataSet can contain one
or many DataTable objects You can define parent/child relationships between the
DataTable objects contained in a DataSet
For example, the page in Listing 19.33 contains a TreeView control The TreeView displays
a list of movie categories and, beneath each movie category, a list of matching movies (see
Figure 19.18)
Trang 6LISTING 19.33 ShowDataSet.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”>
void Page_Load()
{
if (!Page.IsPostBack)
BuildTree();
}
void BuildTree()
{
// Create Connection
string connectionString =
WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString;
SqlConnection con = new SqlConnection(connectionString);
FIGURE 19.18 Building a TreeView from a DataSet
Trang 7// Create Movie Categories DataAdapter
SqlDataAdapter dadCategories = new SqlDataAdapter(
“SELECT Id,Name FROM MovieCategories”, con);
// Create Movies DataAdapter
SqlDataAdapter dadMovies = new SqlDataAdapter(
“SELECT Title,CategoryId FROM Movies”, con);
// Add the DataTables to the DataSet
DataSet dstMovies = new DataSet();
using (con)
{
con.Open();
dadCategories.Fill(dstMovies, “Categories”);
dadMovies.Fill(dstMovies, “Movies”);
}
// Add a DataRelation
dstMovies.Relations.Add(“Children”,
dstMovies.Tables[“Categories”].Columns[“Id”],
dstMovies.Tables[“Movies”].Columns[“CategoryId”]);
// Add the Movie Category nodes
foreach (DataRow categoryRow in dstMovies.Tables[“Categories”].Rows)
{
string name = (string)categoryRow[“Name”];
TreeNode catNode = new TreeNode(name);
TreeView1.Nodes.Add(catNode);
// Get matching movies
DataRow[] movieRows = categoryRow.GetChildRows(“Children”);
foreach (DataRow movieRow in movieRows)
{
string title = (string)movieRow[“Title”];
TreeNode movieNode = new TreeNode(title);
catNode.ChildNodes.Add(movieNode);
}
}
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show DataSet</title>
</head>
<body>
Trang 8<form id=”form1” runat=”server”>
<div>
<asp:TreeView
id=”TreeView1”
Runat=”server” />
</div>
</form>
</body>
</html>
The TreeView is built programmatically In the BuildTree() method, a DataSet is created
that contains two DataTable objects The first DataTable represents the MovieCategories
database table, and the second DataTable represents the Movies database table A
parent/child relationship is created between the two DataTable objects with the help of a
DataRelation
The DataRelation is used to get the movies that match each movie category The
DataRow.GetChildRows() method is called to retrieve the movies that match a particular
movie category
Executing Asynchronous Database Commands
Normally, when you execute a database command, the thread executing the command
must wait until the command finishes before executing any additional code In other
words, normally, when you execute a database command, the thread is blocked
When you take advantage of asynchronous commands, on the other hand, the database
command is executed on another thread so that the current thread can continue
perform-ing other work For example, you can use the current thread to execute yet another
data-base command
There are two reasons that you might want to use asynchronous database commands
when building an ASP.NET page First, executing multiple database commands
simultane-ously can significantly improve your application’s performance This is especially true
when the database commands are executed against different database servers
Second, the ASP.NET Framework uses a limited thread pool to service page requests When
ASP.NET Framework receives a request for a page, it assigns a thread to handle the request
If ASP.NET Framework runs out of threads, the request is queued until a thread becomes
available If too many threads are queued, the framework rejects the page request with a
503—Server Too Busy response code
Trang 9If you execute a database command asynchronously, the current thread is released back
into the thread pool so that it can be used to service another page request While the
asynchronous database command is executing, ASP.NET Framework can devote its
atten-tion to handling other page requests When the asynchronous command completes, the
framework reassigns a thread to the original request and the page finishes executing
NOTE
You can configure the ASP.NET thread pool with the httpRuntime element in the web
configuration file You can modify the appRequestQueueLimit, minFreeThreads, and
minLocalRequestFreeThreads attributes to control how many requests ASP.NET
Framework queues before giving up and sending an error
There are two parts to this task undertaken in this section A data access component
that supports asynchronous ADO.NET methods must be created and an ASP.NET page
that executes asynchronously
Using Asynchronous ADO.NET Methods
There are asynchronous versions of several ADO.NET methods These methods come in
pairs: a Begin and End method For example, the SqlCommand object supports the following
asynchronous methods:
BeginExecuteNonQuery()
EndExecuteNonQuery()
BeginExecuteReader()
EndExecuteReader()
BeginExecuteXmlReader()
EndExecuteXmlReader()
The idea is that when you execute the Begin method, the asynchronous task is started on
a separate thread When the method finishes executing, you can use the End method to
get the results
To use these asynchronous methods, you must use a special attribute in your connection
string: the Asynchronous Processing=true attribute
The data access component in Listing 19.34 contains a BeginGetMovies() and
EndGetMovies() method that fetches movies from the Movies database table
asynchro-nously These methods use the ADO.NET BeginExecuteReader() and EndExecuteReader()
to fetch a DataReader asynchronously
Trang 10LISTING 19.34 App_Code\AsyncDataLayer.cs
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
using System.Collections.Generic;
public class AsyncDataLayer
{
private static readonly string _connectionString;
private SqlCommand _cmdMovies;
public IAsyncResult BeginGetMovies(AsyncCallback callback, Object state)
{
SqlConnection con = new SqlConnection(_connectionString);
_cmdMovies = new SqlCommand(
“WAITFOR DELAY ‘0:0:01’;SELECT Title,Director FROM Movies”, con);
con.Open();
return _cmdMovies.BeginExecuteReader(callback, state,
CommandBehavior.CloseConnection);
}
public List<AsyncDataLayer.Movie> EndGetMovies(IAsyncResult result)
{
List<AsyncDataLayer.Movie> results = new List<AsyncDataLayer.Movie>();
SqlDataReader reader = _cmdMovies.EndExecuteReader(result);
while (reader.Read())
{
AsyncDataLayer.Movie newMovie = new AsyncDataLayer.Movie();
newMovie.Title = (string)reader[“Title”];
newMovie.Director = (string)reader[“Director”];
results.Add(newMovie);
}
return results;
}
static AsyncDataLayer()
{
_connectionString =
WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString
+ “;Asynchronous Processing=true”;
}
public class Movie
{