Looking to Reprint this content?
By Bill Hamilton, Matthew MacDonald
Price: $44.95 USD
£31.95 GBP
Cover | Table of Contents | Colophon
Connection
Command
Connection
Command
DataReader
ParameterCollection
Command and the
mappings of both table and column names to the
DataSet columns.
Parameter
Transaction
Connection, Command,
DataReader, Transaction,
ParameterCollection, and
Parameter classes.
DataSet,
DataTable, DataColumn,
DataRow, Constraint,
DataRelationship, and DataView
classes.
DataAdapter class to bridge the data source and
disconnected classes by way of the connected classes. The
DataAdapter is an abstraction of the connected
classes that simplifies filling the disconnected
DataSet or DataTable classes
with data from the data source and updating the data source to
reflect any changes made to the disconnected data. Figure 1-1 shows the relationship between the connected
and disconnected classes in ADO.NET.
Connection
System.Data.SqlClient namespace.
System.Data.SqlClient namespace.
System.Data.OleDb namespace.
DataSet through the
IDataAdapter interface, and possibly the
IDataParameter interface for parameterized
queries. Such a minimal data provider allows a
DataSet to be loaded with data from the data
source, the modification of data within the
DataSet, and the reconciliation of the changed
DataSet with the data source. A minimal provider
can support clients that deal primarily with a disconnected data,
thereby functioning as a bridge between the
Connection object.
Connection objects are one of the simplest
components in ADO.NET, but they encapsulate a fair bit of lower-level
functionality, including user authentication information, a
connection pooling mechanism, and a network connection (assuming the
data source is located on a separate computer).
Connection object and the connection
string settings you can configure. We'll also
consider some finer points, including connection pooling—a key
to highly scalable database applications—and connection events.
Connection object, like all provider-specific
ADO.NET objects, comes in more than one version. You use the version
that's tailored for your specific data source. Here
are two examples:
System.Data.SqlClient.SqlConnection allows you to
connect to a SQL Server database (Version 7.0 or later).
System.Data.OleDb.OleDbConnection allows you to
connect to almost any data source with an associated OLE DB provider.
Connection
object that accesses relational databases implements the common
System.Data.IDbConnection interface. By looking at
the IDbConnection interface,
you'll quickly see the small set of properties and
methods that every Connection object is guaranteed
to support (see Tables 3-1 and 3-2). The most important of these are
the Close( ) and Open( )
methods, and the ConnectionString property, which
specifies a variety of options about the data source and how to
connect to it. All IDbConnection properties are
read-only, except ConnectionString.
Connection object, like all provider-specific
ADO.NET objects, comes in more than one version. You use the version
that's tailored for your specific data source. Here
are two examples:
System.Data.SqlClient.SqlConnection allows you to
connect to a SQL Server database (Version 7.0 or later).
System.Data.OleDb.OleDbConnection allows you to
connect to almost any data source with an associated OLE DB provider.
Connection
object that accesses relational databases implements the common
System.Data.IDbConnection interface. By looking at
the IDbConnection interface,
you'll quickly see the small set of properties and
methods that every Connection object is guaranteed
to support (see Tables 3-1 and 3-2). The most important of these are
the Close( ) and Open( )
methods, and the ConnectionString property, which
specifies a variety of options about the data source and how to
connect to it. All IDbConnection properties are
read-only, except ConnectionString.
|
Member
|
Description
|
|---|---|
ConnectionString |
A string with name-value pairs of connection settings. These settings
often include information such as the user to log in and the location
of the database server. This is the only writeable property.
|
ConnectionTimeout |
The time to wait for a connection to open before failing with a
provider-specific exception (such as |
ConnectionString property.
ConnectionString contains a series of
name/value settings delimited by
semicolons (;). The
order of these settings is unimportant, as is the
capitalization. Taken together,
they specify the information needed to create a connection. Table 3-3 describes some settings you can use.
Parameters that are used for connection pooling are omitted; they are
discussed later in this chapter.
|
Parameter
|
Description
|
|---|---|
AttachDBFilename / Initial File Name |
Used only if you want to connect to an attachable database file (for
example, an .mdf file that
isn't registered with the database system).
Normally, you use the
Initial Catalog parameter
instead.
|
Connect Timeout / Connection Timeout |
The length of time (in seconds) to wait for a connection to the
server before terminating the attempt and generating an error.
Defaults to 15 seconds, and 0 seconds represents an infinite wait.
|
Connection object required for your
data source, apply the appropriate connection string settings, and
open the connection. In Example 3-1, a connection is
created to a SQL Server database on the local computer using
integrated authentication. The code opens the connection, tests its
state, and closes it.
// ConnectionTest.cs - Opens and verifies a connection
using System;
using System.Data.SqlClient;
public class ConnectionTest
{
public static void Main()
{
SqlConnection con = new SqlConnection("Data Source=localhost;" +
"Initial Catalog=Northwind;Integrated Security=SSPI");
con.Open();
Console.WriteLine("Connection is " + con.State.ToString());
con.Close();
Console.WriteLine("Connection is " + con.State.ToString());
}
}
Connection is Open Connection is Closed
Connection objects expose only two events. The
InfoMessage
event can retrieve warnings and other
messages from a data source. Generally, you use this event if you
wish to receive specific information messages that
don't correspond to errors (in SQL Server, these are
messages with a severity of 10 or less). You can't
use this event to be informed about errors, which simply causes the
Connection object to throw an exception.
EventArgs object, such as
OleDbInfoMessageEventArgs or
SqlInfoMessageEventArgs.
Command
object. In this chapter, we introduce the Command
object in detail. You'll learn how to define a
command and use it to execute nonquery commands such as direct record
updates, insertions, and deletions. You'll also
learn how to use parameterized commands and commands that access
stored procedures.
Command object is the heart of data processing
with ADO.NET. Typically, the Command object wraps
a SQL statement or a call to a stored procedure. For example, you
might use a Command object to execute a SQL
UPDATE, DELETE, INSERT, or SELECT statement. However, ADO.NET
providers that don't represent databases may use
their own nomenclature. The only rule is that the
Command.CommandText
property, which defines the command,
must be a string.
Connection object, the
Command object is specific to the data provider.
Two examples are:
System.Data.SqlClient.SqlCommand executes commands
against SQL Server Version 7.0 or later.
System.Data.OleDb.OleDbCommand executes commands
against an OLE DB data provider.
Command object implements the
System.Data.IDbCommand
interface. That means it is
guaranteed to support the members shown in Tables 4-1 and 4-2. At a
minimum, you must set the CommandText and a
reference to a valid Connection before using a
Command. In addition, you must modify the
CommandType default value if you wish to invoke a
stored procedure.
|
Member
|
|---|
Command object is the heart of data processing
with ADO.NET. Typically, the Command object wraps
a SQL statement or a call to a stored procedure. For example, you
might use a Command object to execute a SQL
UPDATE, DELETE, INSERT, or SELECT statement. However, ADO.NET
providers that don't represent databases may use
their own nomenclature. The only rule is that the
Command.CommandText
property, which defines the command,
must be a string.
Connection object, the
Command object is specific to the data provider.
Two examples are:
System.Data.SqlClient.SqlCommand executes commands
against SQL Server Version 7.0 or later.
System.Data.OleDb.OleDbCommand executes commands
against an OLE DB data provider.
Command object implements the
System.Data.IDbCommand
interface. That means it is
guaranteed to support the members shown in Tables 4-1 and 4-2. At a
minimum, you must set the CommandText and a
reference to a valid Connection before using a
Command. In addition, you must modify the
CommandType default value if you wish to invoke a
stored procedure.
|
Member
|
Description
|
|---|---|
CommandText |
Contains the SQL statement, stored
procedure name, or table name. For an unusual provider (one that
doesn't work with a database), this can contain
something entirely different and proprietary; the only requirement is
that is must be formatted as a string.
|
Command object, you have the choice of
several constructors. The most useful accepts a
CommandText value and a
Connection. Here's an example
with the SqlCommand class:
SqlConnection con = new SqlConnection(connectionString); SqlCommand cmd = new SqlCommand(commandText, con);
ExecuteNonQuery( )
, ExecuteReader( ),
and ExecuteScalar( ). You choose one of these
methods, depending on the type of command you are executing. For
example, ExecuteReader( ) returns a
DataReader and provides read-only access to query
results. We examine the DataReader in Chapter 5.
ExecuteXmlReader( )
method that retrieves data as an XML
document. We'll examine this specialized version in
Chapter 17, which considers
ADO.NET's support for XML.
CommandText property with the full
SQL statement, open a connection, and invoke the
ExecuteNonQuery( )
method. The next sections consider
examples that update, delete, and insert records.
UPDATE table SET update_expression WHERE search_condition
Command object:
parameters.
Command parameters are conceptually the same as method parameters in
an ordinary piece of .NET code. The most common type of parameter is
an input parameter
, which carries information from your
application to the data source. You can use an input parameter when
calling a stored procedure or when coding a parameterized query. In
addition, you can use output
parameters
, which return information from the
data source to your code, or bidirectional
parameters
, which transmit values in both
directions. Output and bidirectional parameters are used only when
you are making stored procedure calls.
Command object has an associated collection
of Parameter
objects (referenced by its
Parameters
property). The
Parameter object is a provider-specific object,
which means a SqlCommand uses a
SqlParameter, an OleDbCommand
uses an OleDbParameter, and so on.
Parameter object, you must
specify a parameter name, and the exact data type for the information
it will contain. For the managed OLE DB provider, you specify data
types using the System.Data.OleDb.OleDbType
enumeration. For the SQL Server data provider, you use the
System.Data.SqlDbType enumeration. If the data
type is a variable-length field such as a string or binary field, you
also need to indicate the field length.
SqlParameter object named
@MyParamUPDATE Categories SET CategoryName='Beverages' WHERE CategoryID=1
UPDATE Categories SET CategoryName=@CategoryName WHERE CategoryID=@CategoryID
Parameter objects to the
Command, with the names
@CategoryName and @CategoryID.
Now set the values for both these Parameter
objects to Beverages and 1, respectively, and invoke the command.
Example 4-3 shows a full example that rewrites Example 4-1 to use a parameterized command.
// ParameterizedUpdateSQL.cs - Updates a single Category record
using System;
using System.Data;
using System.Data.SqlClient;
public class UpdateRecord
{
public static void Main()
{
string connectionString = "Data Source=localhost;" +
"Initial Catalog=Northwind;Integrated Security=SSPI";
string SQL = "UPDATE Categories SET CategoryName=@CategoryName " +
"WHERE CategoryID=@CategoryID";
// Create ADO.NET objects.
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(SQL, con);
SqlParameter param;
param = cmd.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15);
param.Value = "Beverages";
param = cmd.Parameters.Add("@CategoryID", SqlDbType.Int);
param.Value = 1;
// Execute the command.
con.Open();
int rowsAffected = cmd.ExecuteNonQuery();
con.Close();
// Display the result of the operation.
Console.WriteLine(rowsAffected.ToString() + " row(s) affected");
}
}Command and executing it with the
ExecuteNonQuery( )
method. Example 4-9
shows a trivial example that uses the CREATE TABLE statement to
create a single table with two columns. In this case, the table is
created in the Northwind database because that's the
initial database selected when the connection is opened.
// DDL.cs - Inserts a new table
using System;
using System.Data.SqlClient;
public class UpdateRecord
{
public static void Main()
{
string connectionString = "Data Source=localhost;" +
"Initial Catalog=Northwind;Integrated Security=SSPI";
string SQL = "CREATE TABLE Users ("+
"UserName nvarchar(20), Password nvarchar(20) )";
// Create ADO.NET objects.
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(SQL, con);
// Execute the command.
con.Open();
int rowsAffected = cmd.ExecuteNonQuery();
con.Close();
// Display the result of the operation.
Console.WriteLine(rowsAffected.ToString() + " row(s) affected");
}
}DataSet object, as discussed in later chapters,
and with the DataReader, which is the focus of
this chapter.
DataReader is little more than a thin wrapper
over a cursor that retrieves query results in a read-only,
forward-only stream of information. The DataReader
won't let you perform updates, see the results of
live updates, or move back and forth through a result set as a
server-side cursor does in traditional ADO programming. However, what
you sacrifice in flexibility, you gain in performance. Because this
cursor consumes few server resources and requires relatively little
locking, the DataReader is always a
performance-optimal way to
retrieve data.
DataReader to retrieve data and schema
information, how to handle specialized data types such as binary
large objects (BLOBs), and how to write code that can access any type
of data source with the DataReader.
DataReader for every data provider. Here are two
examples:
System.Data.SqlClient.SqlDataReader provides
forward-only, read-only access to a SQL Server database (Version 7.0
or later).
System.Data.OleDb.OleDbDataReader provides
forward-only, read-only access to a data source exposed through an
OLE DB provider.
DataReader object implements the
System.Data.IDataReader and the
System.Data.IDataRecord interfaces. The
IDataReader interface provides the core
methods shown in Table 5-1, such as Read( )
,
which retrieves a single row from the stream. The
DataReader for every data provider. Here are two
examples:
System.Data.SqlClient.SqlDataReader provides
forward-only, read-only access to a SQL Server database (Version 7.0
or later).
System.Data.OleDb.OleDbDataReader provides
forward-only, read-only access to a data source exposed through an
OLE DB provider.
DataReader object implements the
System.Data.IDataReader and the
System.Data.IDataRecord interfaces. The
IDataReader interface provides the core
methods shown in Table 5-1, such as Read( )
,
which retrieves a single row from the stream. The
IDataRecord interface provides the indexer for the
DataReader and allows you to access the column
values for the current row by column name or ordinal number.
|
Member
|
Description
|
|---|---|
Close( ) |
Closes the
DataReader but not the underlying
Connection. This allows you to use the
Connection for another task.
|
GetSchemaTable( ) |
Retrieves a
DataTable object with information
about the schema for the current result set.
|
NextResult( ) |
When executing a Command that returns multiple
result sets, you must use NextResult( ) to move
from one result set to another. This method returns
true |
Command and
DataReader, you need to use the SELECT statement,
which identifies the table and rows you want to retrieve, the filter
and ordering clauses, and any table joins:
SELECT columns FROM tables WHERE search_condition ORDER BY order_expression ASC | DESC
// DataReaderFillForm.cs - Fills a ListBox
using System;
using System.Windows.Forms;
using System.Data.SqlClient;
public class DataReaderTest : Form
{
private ListBox lstNames;
private string connectionString = "Data Source=localhost;" +
"Initial Catalog=Northwind;Integrated Security=SSPI";
public DataReaderTest()
{
lstNames = new ListBox();
lstNames.Dock = DockStyle.Fill;
Controls.Add(lstNames);
Load += new EventHandler(DataReaderTest_Load);
}
public static void Main()
{
DataReaderTest t = new DataReaderTest();
Application.Run(t);
}
private void DataReaderTest_Load(object sender, System.EventArgs e)
{
string SQL = "SELECT ContactName FROM Customers";
// Create ADO.NET objects.
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(SQL, con);
SqlDataReader r = null;
// Execute the command.
try
{
con.Open();
r = cmd.ExecuteReader();
// Iterate over the results.
while (r.Read())
{
lstNames.Items.Add(r["ContactName"]);
}
}
catch (Exception err)
{
MessageBox.Show(err.ToString());
}
finally
{
if (r != null) r.Close();
con.Close();
}
}
}CustOrderHist procedure, which returns the total
number of products a given customer has ordered, grouped by product
name.
CustOrderHist stored procedure. It defines one
parameter (shown in the first line), called
@CustomerID:
CREATE PROCEDURE CustOrderHist @CustomerID nchar(5)
AS
SELECT ProductName, Total=SUM(Quantity)
FROM Products P, [Order Details] OD, Orders O, Customers C
WHERE C.CustomerID = @CustomerID AND
C.CustomerID = O.CustomerID AND
O.OrderID = OD.OrderID AND OD.ProductID = P.ProductID
GROUP BY ProductName
GO
// TotalOrders.cs - Runs the CustOrderHist stored procedure.
using System;
using System.Data;
using System.Data.SqlClient;
public class TotalOrders
{
public static void Main()
{
string connectionString = "Data Source=localhost;" +
"Initial Catalog=Northwind;Integrated Security=SSPI";
string procedure = "CustOrderHist";
// Create ADO.NET objects.
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(procedure, con);
SqlDataReader r;
// Configure command and add parameters.
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter param;
param = cmd.Parameters.Add("@CustomerID", SqlDbType.NChar, 5);
param.Value = "ALFKI";
// Execute the command.
con.Open();
r = cmd.ExecuteReader();
while (r.Read())
{
Console.WriteLine(r["Total"].ToString() + " of " +
r["ProductName"].ToString());
}
con.Close();
}
}DataSet, as you'll learn
in the following chapters. However, even if you
aren't using the DataSet, you may
want to retrieve some sort of schema information from a data source.
With ADO.NET, you have two choices: you can use the
DataReader.GetSchemaTable( )
method to retrieve schema information
about a specific query, or you can explicitly request a schema table
from the data source.
DataReader is open, you can invoke
its GetSchemaTable( ) method to return a
DataTable object with the schema information for
the result set. This DataTable will contain one
row for each column in the result set. Each row will contain a series
of fields with column information, including the data type, column
name, and so on.
// GetSchema.cs - Retrieves a schema table for a query
using System;
using System.Data;
using System.Data.SqlClient;
public class GetSchema
{
public static void Main()
{
string connectionString = "Data Source=localhost;" +
"Initial Catalog=Northwind;Integrated Security=SSPI";
string SQL = "SELECT * FROM CUSTOMERS";
// Create ADO.NET objects.
SqlConnec