The different implementations of the Connection, Command, and DataReader classes are grouped into the following namespaces:.. Because ADO.NET follows the Provider model, all implementati
Trang 1You also learn how to build Microsoft SQL Server database objects, such as stored
proce-dures and user-defined types, by using NET Framework For example, you learn how to
write a Microsoft SQL Server stored procedure, using C# programming language
NOTE
If you don’t want to get your hands dirty touching any actual SQL or ADO.NET code, skip
this chapter and read Chapter 20, “Data Access with LINQ to SQL.” LINQ to SQL
enables you to access the database without writing any ADO.NET or SQL code
Connected Data Access
The ADO.NET Framework encompasses a huge number of classes However, at its heart, it
actually consists of the following three classes:
Connection—Enables you to represent a connection to a data source
Command—Enables you to execute a command against a data source
DataReader—Enables you to represent data retrieved from a data source
Most of the other classes in ADO.NET Framework are built from these three classes These
three classes provide you with the fundamental methods of working with database data
They enable you to connect to a database, execute commands against a database, and
represent the data returned from a database
Now that you understand the importance of these three classes, it’s safe to tell you that
they don’t actually exist ADO.NET uses the Provider model You use different sets of
ADO.NET classes for communicating with different data sources
For example, there is no such thing as the Connection class Instead, there are the
SqlConnection, OracleConnection, OleDbConnection, and ODBCConnection classes You use
different Connection classes to connect to different data sources
The different implementations of the Connection, Command, and DataReader classes are
grouped into the following namespaces:
System.Data.SqlClient—Contains ADO.NET classes for connecting to Microsoft
SQL Server version 7.0 or higher
System.Data.OleDb—Contains ADO.NET classes for connecting to a data source with
an OLEDB provider
System.Data.Odbc—Contains ADO.NET classes for connecting to a data source with
an ODBC driver
System.Data.OracleClient—Contains ADO.NET classes for connecting to an Oracle
database (requires Oracle 8i Release 3/8.1.7 Client or later)
System.Data.SqlServerCe—Contains ADO.NET classes for connecting to SQL Server
Mobile
Trang 2If you connect to Microsoft SQL Server 7.0 or higher, you should always use the classes
from the SqlClient namespace These classes provide the best performance because they
connect directly to SQL Server at the level of the Tabular Data Stream (the low-level
proto-col that Microsoft SQL Server uses to communicate with applications)
Of course, there are other databases in the world than Microsoft SQL Server If you are
communicating with an Oracle database, you should use the classes from the
OracleClient namespace If you are communicating with another type of database, you
need to use the classes from either the OleDb or Odbc namespaces Just about every
data-base created by man has either an OLEDB provider or an ODBC driver
Because ADO.NET follows the Provider model, all implementations of the Connection,
Command, and DataReader classes inherit from a set of base classes Here is a list of these
base classes:
DbConnection—The base class for all Connection classes
DbCommand—The base class for all Command classes
DbDataReader—The base class for all DataReader classes
These base classes are contained in the System.Data.Common namespace
All the sample code in this chapter assumes that you work with Microsoft SQL Server
Therefore, all the sample code uses the classes from the SqlClient namespace However,
because ADO.NET uses the Provider model, the methods that you would use to work with
another database are similar to the methods described in this chapter
NOTE
Before you can use the classes from the SqlClient namespaces in your components
and pages, you need to import the System.Data.SqlClient namespace
Before we examine the Connection, Command, and DataReader classes in detail, let’s look at
how you can build a simple data access component with these classes The component in
Listing 19.1, named Movie1, includes a method named GetAll() that returns every record
from the Movies database table
LISTING 19.1 App_Code\Movie1.cs
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
using System.Collections.Generic;
public class Movie1
{
private static readonly string _connectionString;
Trang 3private string _title;
private string _director;
public string Title
{
get { return _title; }
set { _title = value; }
}
public string Director
{
get { return _director; }
set { _director = value; }
}
public List<Movie1> GetAll()
{
List<Movie1> results = new List<Movie1>();
SqlConnection con = new SqlConnection(_connectionString);
SqlCommand cmd = new SqlCommand(“SELECT Title,Director FROM Movies”, con);
using (con)
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Movie1 newMovie = new Movie1();
newMovie.Title = (string)reader[“Title”];
newMovie.Director = (string)reader[“Director”];
results.Add(newMovie);
}
}
return results;
}
static Movie1()
{
_connectionString =
WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString;
}
}
In Listing 19.1, a SqlConnection object represents a connection to a Microsoft SQL Server
database A SqlCommand object represents a SQL SELECT command The results of executing
the command are represented with a SqlDataReader
Trang 4Each row returned by the SELECT command is retrieved by a call to the
SqlDataReader.Read() method from within a While loop When the last row is retrieved
from the SELECT command, the SqlDataReader.Read() method returns False and the
While loop ends
Each row retrieved from the database is added to a List collection An instance of the
Movie1 class represents each record
The page in Listing 19.2 uses a GridView and ObjectDataSource control to display the
records returned by the Movie1 data access component (see Figure 19.1)
FIGURE 19.1 Displaying movie records
LISTING 19.2 ShowMovie1.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 Movie1</title>
Trang 5</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie1”
SelectMethod=”GetAll”
Runat=”server” />
</div>
</form>
</body>
</html>
Using the Connection Object
The Connection object represents a connection to a data source When you instantiate a
Connection, you pass a connection string to the constructor, which contains information
about the location and security credentials required for connecting to the data source
For example, the following statement creates a SqlConnection that represents a
connec-tion to a Microsoft SQL Server database named Pubs that is located on the local machine:
SqlConnection con = new SqlConnection(“Data Source=localhost;Integrated
➥Security=True;Initial Catalog=Pubs”);
For legacy reasons, there are a number of ways to write a connection string that does
exactly the same thing For example, the keywords Data Source, Server, Address, Addr,
and Network Address are all synonyms You can use any of these keywords to specify the
location of the database server
NOTE
You can use the SqlConnectionStringBuilder class to convert any connection string
into canonical syntax For example, this class replaces the keyword Server with the
keyword Data Source in a connection string
Trang 6Before you execute any commands against the data source, you first must open the
connection After you finish executing commands, you should close the connection as
quickly as possible
A database connection is a valuable resource Strive to open database connections as late
as possible, and close database connections as early as possible Furthermore, always
include error handling code to make sure that a database connection gets closed even
when there is an exception
For example, you can take advantage of the Using statement to force a connection to close
even when an exception is raised, like this:
SqlConnection con = new SqlConnection(“Data Source=localhost;
Integrated Security=True;Initial Catalog=Pubs”);
SqlCommand cmd = new SqlCommand(“INSERT Titles (Title) VALUES (‘Some Title’)”, con);
using (con)
{
con.Open();
cmd.ExecuteNonQuery();
}
The using statement forces the connection to close, regardless of whether an error occurs
when a command is executed against the database The using statement also disposes of
the Connection object (If you need to reuse the Connection, you need to reinitialize it.)
Alternatively, you can use a try catch statement to force a connection to close like this:
SqlConnection con = new SqlConnection(“Data Source=localhost;Integrated
Security=True;Initial Catalog=Pubs”);
SqlCommand cmd = new SqlCommand(“INSERT Titles (Title) VALUES (‘Some Title’)”, con);
try
{
con.Open();
cmd.ExecuteNonQuery();
}
finally
{
con.Close();
}
The finally clause in this try catch statement forces the database connection to close
both when there are no errors and when there are errors
Retrieving Provider Statistics
When you use the SqlConnection object, you can retrieve statistics about the database
commands executed with the connection For example, you can retrieve statistics on total
execution time
Trang 7The GetAll() method exposed by the component in Listing 19.3 includes a parameter
named executionTime After the database command executes, the value of executionTime
is retrieved from the Connection statistics
LISTING 19.3 App_Code\Movie2.cs
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
using System.Collections;
using System.Collections.Generic;
public class Movie2
{
private static readonly string _connectionString;
private string _title;
private string _director;
public string Title
{
get { return _title; }
set { _title = value; }
}
public string Director
{
get { return _director; }
set { _director = value; }
}
public List<Movie2> GetAll(out long executionTime)
{
List<Movie2> results = new List<Movie2>();
SqlConnection con = new SqlConnection(_connectionString);
SqlCommand cmd = new SqlCommand(“WAITFOR DELAY ‘0:0:03’;SELECT Title,Director
FROM Movies”, con);
con.StatisticsEnabled = true;
using (con)
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Trang 8Movie2 newMovie = new Movie2();
newMovie.Title = (string)reader[“Title”];
newMovie.Director = (string)reader[“Director”];
results.Add(newMovie);
}
}
IDictionary stats = con.RetrieveStatistics();
executionTime = (long)stats[“ExecutionTime”];
return results;
}
static Movie2()
{
_connectionString =
WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString;
}
}
In Listing 19.3, the SqlConnection.StatisticsEnabled property is set to the value True
You must enable statistics before you can gather statistics After the command executes, a
dictionary of statistics is retrieved with the SqlConnection.RetrieveStatistics()
method Finally, you retrieve the executionTime by looking up the ExecutionTime key in
the dictionary
NOTE
In Listing 19.3, the SQL WAITFOR statement is used to pause the execution of the
SELECT command for three seconds so that a more interesting execution time is
retrieved from the ExecutionTime statistic Because the SELECT command is such a
simple command, if you don’t add a delay, you often receive an execution time of 0
milliseconds
The page in Listing 19.4 illustrates how you can use this component to display both the
results of a database query and the database query execution time (see Figure 19.2)
Trang 9LISTING 19.4 ShowMovie2.aspx
<%@ Page Language=”C#” %>
<!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 srcMovies_Selected(object sender,
➥ObjectDataSourceStatusEventArgs e)
{
lblExecutionTime.Text = e.OutputParameters[“executionTime”].ToString();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show Movie2</title>
</head>
<body>
FIGURE 19.2 Displaying execution time statistics
Trang 10<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
DataSourceID=”srcMovies”
Runat=”server” />
<asp:ObjectDataSource
id=”srcMovies”
TypeName=”Movie2”
SelectMethod=”GetAll”
Runat=”server” OnSelected=”srcMovies_Selected”>
<SelectParameters>
<asp:Parameter Name=”executionTime” Type=”Int64” Direction=”Output” />
</SelectParameters>
</asp:ObjectDataSource>
<br />
Execution time was
<asp:Label
id=”lblExecutionTime”
Runat=”server” />
milliseconds
</div>
</form>
</body>
</html>
The SqlConnection object supports the following properties and methods related to
gath-ering statistics:
StatisticsEnabled—Enables you to turn on statistics gathering
RetrieveStatistics()—Enables you to retrieve statistics represented with an
IDictionary collection
ResetStatistics()—Resets all statistics to 0
You can call the RetrieveStatistics() method multiple times on the same
SqlConnection Each time you call the method, you get another snapshot of the
Connection statistics