LISTING 19.23 ShowDataLayer1.aspx void Page_Load { // Get database data List categories = ➥new List; List movies = new List; DataLayer1.GetMovieDatacategories, movies; // Bind the d
Trang 1{
DataLayer1.Movie newMovie = new DataLayer1.Movie();
newMovie.Title = (string)reader[“Title”];
newMovie.CategoryId = (int)reader[“CategoryID”];
movies.Add(newMovie);
}
}
}
static DataLayer1()
{
_connectionString =
WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString;
}
}
The SqlDataReader.NextResult() method is called to advance to the next resultset This
method returns either True or False depending on whether a next resultset exists In
Listing 19.22, it is assumed that there is both a movies category and movies resultset
The page in Listing 19.23 displays the contents of the two database tables in two GridView
controls (see Figure 19.10)
FIGURE 19.10 Displaying two resultsets
Trang 2LISTING 19.23 ShowDataLayer1.aspx
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Collections.Generic” %>
<!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()
{
// Get database data
List<DataLayer1.MovieCategory> categories =
➥new List<DataLayer1.MovieCategory>();
List<DataLayer1.Movie> movies = new List<DataLayer1.Movie>();
DataLayer1.GetMovieData(categories, movies);
// Bind the data
grdCategories.DataSource = categories;
grdCategories.DataBind();
grdMovies.DataSource = movies;
grdMovies.DataBind();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show DataLayer1</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<h1>Movie Categories</h1>
<asp:GridView
id=”grdCategories”
Runat=”server” />
<h1>Movies</h1>
<asp:GridView
id=”grdMovies”
Runat=”server” />
</div>
</form>
</body>
</html>
Trang 3Working with Multiple Active Resultsets
ADO.NET 2.0 introduced a new feature named Multiple Active Results Sets (MARS) In
the previous version of ADO.NET, a database connection could represent only a single
resultset at a time If you take advantage of MARS, you can represent multiple resultsets
with a single database connection Using MARS is valuable in scenarios in which you
need to iterate through a resultset and perform an additional database operation for each
record in the resultset MARS is disabled by default To enable MARS, you must include a
MultipleActiveResultSets=True attribute in a connection string
For example, the page in Listing 19.24 programmatically builds the nodes in a TreeView
control The page displays a list of movie categories and, beneath each movie category, it
displays a list of matching movies (see Figure 19.11)
FIGURE 19.11 Fetching database records with MARS enabled
LISTING 19.24 ShowMARS.aspx
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Data” %>
<%@ Import Namespace=”System.Data.SqlClient” %>
<!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()
{
Trang 4if (!Page.IsPostBack)
BuildTree();
}
void BuildTree()
{
// Create MARS connection
string connectionString = @”MultipleActiveResultSets=True;”
+ @”Data Source=.\SQLExpress;Integrated Security=True;”
+ @”AttachDBFileName=|DataDirectory|MyDatabase.mdf;User Instance=True”;
SqlConnection con = new SqlConnection(connectionString);
// Create Movie Categories command
string cmdCategoriesText = “SELECT Id,Name FROM MovieCategories”;
SqlCommand cmdCategories = new SqlCommand(cmdCategoriesText, con);
// Create Movie command
string cmdMoviesText = “SELECT Title FROM Movies “
+ “WHERE CategoryId=@CategoryID”;
SqlCommand cmdMovies = new SqlCommand(cmdMoviesText, con);
cmdMovies.Parameters.Add(“@CategoryId”, SqlDbType.Int);
using (con)
{
con.Open();
// Iterate through categories
SqlDataReader categories = cmdCategories.ExecuteReader();
while (categories.Read())
{
// Add category node
int id = categories.GetInt32(0);
string name = categories.GetString(1);
TreeNode catNode = new TreeNode(name);
TreeView1.Nodes.Add(catNode);
// Iterate through matching movies
cmdMovies.Parameters[“@CategoryId”].Value = id;
SqlDataReader movies = cmdMovies.ExecuteReader();
while (movies.Read())
{
// Add movie node string title = movies.GetString(0);
TreeNode movieNode = new TreeNode(title);
catNode.ChildNodes.Add(movieNode);
Trang 5} movies.Close();
}
}
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show MARS</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:TreeView
id=”TreeView1”
Runat=”server” />
</div>
</form>
</body>
</html>
The MultipleActiveResultSets attribute is included in the connection string used to
open the database connection If MARS were not enabled, you couldn’t loop through the
interior SqlDataReader that represents the matching movies while the containing
SqlDataReader that represents the movie categories is open
Disconnected Data Access
The ADO.NET Framework supports two models of data access In the first part of this
chapter, you saw how you can use the SqlConnection, SqlCommand, and SqlDataReader
objects to connect to a database and retrieve data When you read data from a database by
using a SqlDataReader object, an open connection must be maintained between your
application and the database
In this section, we examine the second model of data access supported by ADO.NET: the
disconnected model When you use the objects discussed in this section, you do not need
to keep a connection to the database open
This section discusses four new ADO.NET objects:
in-memory database and back again
Trang 6The ADO.NET objects discussed in this section are built on top of the ADO.NET objects
discussed in the previous section For example, behind the scenes, the DataAdapter uses a
DataReader to retrieve data from a database
The advantage of using the objects discussed in this section is that they provide you with
more functionality For example, you can filter and sort the rows represented by a
DataView Furthermore, you can use the DataTable object to track changes made to
records and accept or reject the changes
The big disadvantage of using the objects discussed in this section is that they tend to be
slower and more resource intensive Retrieving 500 records with a DataReader is much
faster than retrieving 500 records with a DataAdapter
NOTE
For detailed performance comparisons between the DataReader and DataAdapter, see
Priya Dhawan’s article at the Microsoft MSDN website (msdn.Microsoft.com),
“Performance Comparison: Data Access Techniques.”
Therefore, unless you need to use any of the specialized functionality supported by these
objects, my recommendation is that you stick with the objects discussed in the first part
of this chapter when accessing a database In other words, DataReaders are good and
DataAdapters are bad
Using the DataAdapter Object
The DataAdapter acts as the bridge between an in-memory database table and a physical
database table You use the DataAdapter to retrieve data from a database and populate a
DataTable You also use a DataAdapter to push changes that you have made to a DataTable
back to the physical database
The component in Listing 19.25 illustrates how you can use a SqlDataAdapter to populate
a DataTable
Trang 7LISTING 19.25 App_Code\Movie8.cs
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
using System.Collections.Generic;
public class Movie8
{
private static readonly string _connectionString;
public DataTable GetAll()
{
// Initialize the DataAdapter
SqlDataAdapter dad = new SqlDataAdapter(
“SELECT Title,Director FROM Movies”, _connectionString);
// Create a DataTable
DataTable dtblMovies = new DataTable();
// Populate the DataTable
dad.Fill(dtblMovies);
// Return results
return dtblMovies;
}
static Movie8()
{
_connectionString =
WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString;
}
}
The page in Listing 19.26 contains a GridView that is bound to an ObjectDataSource
that represents the component in Listing 19.25 (see Figure 19.12)
Trang 8LISTING 19.26 ShowMovie8.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show Movie8</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie8”
FIGURE 19.12 Displaying data with a DataAdapter
Trang 9SelectMethod=”GetAll”
Runat=”server” />
</div>
</form>
</body>
</html>
A SqlConnection is never explicitly created in the component in Listing 19.25 When you
call the SqlDataAdapter object’s Fill() method, the SqlDataAdapter automatically creates
and opens a connection After the data is fetched from the database, the Fill() method
automatically closes the connection
You don’t need to wrap the call to the Fill() method within a Using or Try Catch
statement Internally, the SqlDataAdapter uses a Try Catch statement to ensure that its
connection gets closed
Opening and closing a database connection is a slow operation If you know that you will
need to perform another database operation after using the SqlDataAdapter, you should
explicitly create a SqlConnection and open it like this:
SqlConnection con = new SqlConnection( connection string );
SqlDataAdapter dad = new SqlDataAdapter(“SELECT Title,Director FROM Movies”, con);
using (con)
{
con.Open();
dad.Fill(dtblMovies);
Perform other database operations with connection
}
If a SqlConnection is already open when you call the Fill() method, it doesn’t close it
In other words, the Fill() method maintains the state of the connection
Performing Batch Updates
You can think of a SqlDataAdapter as a collection of four SqlCommand objects:
You can use a DataAdapter not only when retrieving data from a database, but also when
updating, inserting, and deleting data from a database If you call a SqlDataAdapter
object’s Update() method, and pass the method a DataTable, the SqlDataAdapter calls its
UpdateCommand, InsertCommand, and DeleteCommand to make changes to the database
Trang 10You can assign a SqlCommand object to each of the four properties of the SqlDataAdapter
Alternatively, you can use the SqlCommandBuilder object to create the UpdateCommand,
InsertCommand, and DeleteCommand for you The SqlCommandBuilder class takes a
SqlDataAdapter that has a SELECT command and generates the other three commands
automatically
For example, the page in Listing 19.27 displays all the records from the Movies database
table in a spreadsheet created with a Repeater control (see Figure 19.13) If you make
changes to the data and click the Update button, the Movies database table is updated
with the changes
FIGURE 19.13 Batch updating database records
LISTING 19.27 ShowDataAdapterUpdate.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”>
private SqlDataAdapter dad;
private DataTable dtblMovies;