169Chapter 11 Making External Data Available Locally After completing this chapter, you will be able to: ■ ■ Load external data into a DataTable or DataSet ■ ■ Return updated DataSet co
Trang 1166 Microsoft ADO NET 4 Step by Step
4 Just after the “Read the next set, which contains the orders” comment, add the
follow-ing code:
customerReader.NextResult()
Do While (customerReader.Read = True)
oneOrder = New OrderInfo
oneOrder.ID = CLng(customerReader!ID)
oneOrder.OrderDate = CDate(customerReader!OrderDate)
oneOrder.OrderTotal = CDec(customerReader!Total)
AllOrders.Items.Add(oneOrder)
Loop
This code accesses the records in the second set of results, the SELECT statement for the OrderEntry table, via the NextResult method call.
5 Run the program On the Customer Management form, select a customer from the list
of customers and then click View Orders When the View Orders form appears, it in-cludes content from both SELECT statements returned by the stored procedure.
Summary
This chapter discussed parameters, which are data value objects that help ensure the accuracy and safety of the data being sent to and returned from external data sources Parameterized queries use special SQL statements that include placeholders for each parameter Each
SqlParameter instance defines the name of the parameter, its data type, and its value.
Parameters work with either standard SQL commands or with stored procedures When using them with stored procedures, you can create both input and output stored procedures, sup-porting two-way communications with these custom database functions
Trang 2Chapter 10 Adding Standards to Queries 167 Chapter 10 Quick Reference
Create a parameterized query for SQL Server Create a SQL query string that includes @-prefixed
placeholders.
Create a SqlCommand instance.
Assign the SQL query to the SqlCommand object’s
CommandText property.
Create SqlParameter objects, one for each placeholder
in the query, and add them to the command object’s
Parameters collection.
Set the SqlCommand.Connection property.
Call one of the command object’s Execute methods.
Create a parameterized query for an OLE DB
data source
Create a SQL query string that includes question marks (?) for placeholders.
Create an OleDbCommand instance.
Assign the SQL query to the OleDbCommand object’s
CommandText property.
Create OleDbParameter objects, one for each
placehold-er in the quplacehold-ery, and add them to the command object’s
Parameters collection.
Set the OleDbCommand.Connection property.
Call one of the command object’s Execute methods.
Create an “out” parameter for a stored procedure Create a SqlParameter instance, setting its fields as
needed.
Set the SqlParameter.Direction property to
ParameterDirection.Output.
Trang 4169
Chapter 11
Making External Data Available
Locally
After completing this chapter, you will be able to:
■
■ Load external data into a DataTable or DataSet
■
■ Return updated DataSet content to an external source
■
■ Use SQL statements and stored procedures to manage DataSet content
The disconnected data experience provided by ADO.NET revolves around the DataSet class
and its supporting objects The last few chapters have introduced ways to access external data with ADO.NET, but none of those features took advantage of the disconnected aspects
of the framework Still, part of the promise of ADO.NET is its ability to manage external data
in a disconnected and table-focused way
This chapter introduces the DataAdapter class—the class that fulfills that core data promise The DataAdapter bridges the simple data connectedness exhibited by the DataReader and joins it with the advanced data management features found in the DataSet By creating a few
simple objects and crafting a minimum number of SQL statements, you can safely give your
DataSet the tools needed to keep it and its associated external data source in sync.
Understanding Data Adapters
Data adapters link your external database tables and your local DataSet-managed tables by issuing SQL statements Anytime you need to get data from the database into a DataSet, the adapter must perform a “Fill” operation, issuing a SELECT statement and moving the results into local DataTable instances You can then update the values in those DataTable instances When it’s time to return changes stored in the DataSet to the database, the data adapter’s
“Update” operation sends the relevant INSERT, UPDATE, and DELETE statements to the
da-tabase to bring the external data store into line with local changes Figure 11-1 shows these
components working on a single database table, Customer.
Trang 5170 Microsoft ADO NET 4 Step by Step
Database ADO.NET
Customer DataAdapter
SELECT
Fill Update INSERT
UPDATE DELETE
DataReader Original Data
User Updates Changed Data
Command Objects
DataSet / DataTable
ID
FullName
Address
Phone
…
FIGURE 11-1 The data adapter in action.
As Figure 11-1 makes clear, the DataAdapter manages a lot of complex activity between the database and a DataSet or DataTable It is no exaggeration to say that the DataAdapter
is possibly the most complex part of ADO.NET, especially when you take advantage of all
the flexibility it provides All the classes introduced so far in this book—from DataSet to SqlParameter, from DataRow to DataReader—come into play when creating instances of a
data adapter class
The System.Data.SqlClient.SqlDataAdapter class exposes the SQL Server provider
implemen-tation of the adapter You can also find OLE DB and ODBC variations of the data adapter in
the classes System.Data.OleDb.OleDbDataAdapter and System.Data.Odbc.OdbcDataAdapter, respectively All these classes derive from System.Data.Common.DbDataAdapter, which in turn derives from System.Data.Common.DataAdapter.
Note Although the information in this chapter applies generally to all data adapter implemen-tations, this chapter’s code samples and examples focus specifically on the SQL Server provider version.
SqlDataAdapter provides three general support features in your application:
■
■ Record retrieval Populating a DataTable with database records represents the
mini-mal functionality of the data adapter Internally, the SqlDataAdapter uses a DataReader instance to retrieve records out of the database, so you must provide it with a SELECT
statement and a connection string Stored procedures that return data rows also work; the adapter will correctly process multiple record sets returned by the query
Trang 6Chapter 11 Making External Data Available Locally 171
■
■ Record updating Moving modified data back to external storage is a little more
in-volved Although the “fill” from the database requires only a basic SELECT statement, the “update” operation requires distinct INSERT, UPDATE, and DELETE statements to
complete its work You can write these by hand or use a “command builder” to
auto-matically generate these statements based on the original SELECT query.
■
■ Table and column name mapping The naming needs of your database tables and
columns may not always mesh with the needs of your application Each data adapter includes a mapping layer that automatically renames tables and columns as needed while data is passed between local and remote storage areas
The remainder of this chapter elaborates on these three data adapter features
Moving Data from Source to Memory
The SqlDataAdapter.Fill method requests data from SQL Server using a valid SELECT
state-ment or a data-selection stored procedure After it accesses the data through an internal
SqlDataReader, it moves the records into the DataTable or DataSet of your choice.
Moving Data into a DataTable
To move data from a database table into a DataTable instance, set up a new SqlDataAdapter object and call its Fill method, passing it the instance of the DataTable.
C#
DataTable targetTable = new DataTable();
SqlDataAdapter workAdapter = new SqlDataAdapter(
"SELECT * FROM Customer ORDER BY LastName", connectionString);
workAdapter.Fill(targetTable);
Visual Basic
Dim targetTable As New DataTable
Dim workAdapter As New SqlDataAdapter(
"SELECT * FROM Customer ORDER BY LastName", connectionString)
workAdapter.Fill(targetTable)
The data adapter uses the constructor arguments to create a new SqlCommand instance It then assigns this instance to its SelectCommand property, a property that must be set before the SqlDataAdapter can do its data retrieval work.
Trang 7172 Microsoft ADO NET 4 Step by Step
In addition to the two-string constructor variation shown previously, overloaded versions
let you pass in a configured SqlCommand instance, pass in a SQL string and SqlConnection pair, or just leave off the arguments altogether The SqlDataAdapter class has no
connec-tion string or connecconnec-tion properties, so if you don’t provide them with the constructor, you
need to include them with a SqlCommand instance that you assign to the SqlDataAdapter SelectCommand property directly, as shown here:
C#
DataTable targetTable = new DataTable();
using (SqlConnection linkToDB = new SqlConnection(connectionString))
{
SqlDataAdapter workAdapter = new SqlDataAdapter();
workAdapter.SelectCommand = new SqlCommand(
"SELECT * FROM Customer ORDER BY LastName", linkToDB);
workAdapter.Fill(targetTable);
}
Visual Basic
Dim targetTable As New DataTable
Using linkToDB As New SqlConnection(builder.ConnectionString)
Dim workAdapter As New SqlDataAdapter
workAdapter.SelectCommand = New SqlCommand(
"SELECT * FROM Customer ORDER BY LastName", linkToDB)
workAdapter.Fill(targetTable)
End Using
Neither of the preceding examples opened the connection explicitly If the command’s
con-nection isn’t open yet, the Fill method opens it for you—and closes it when the operation
completes
As the data adapter reads the incoming data, it examines the schema of that data and builds
the columns and properties of the DataTable instance as needed If the DataTable already has
matching columns (names and data types), they are used as is Any new columns are created alongside the preexisting columns
Note You can alter this default behavior, as described in this chapter’s “Table and Column
Mapping” section on page 186.
The DataTable.TableName property will be set to “Table,” even if you selected records from
a specific table with a different name To alter the target table’s name, modify its TableName
property after the data load or use the table mapping features discussed later in this chapter
Trang 8Chapter 11 Making External Data Available Locally 173
Because the SqlDataAdapter.SelectCommand property is a standard SqlCommand instance,
you can use any of that command object’s features to access the remote data This includes
adding one or more SqlParameter objects for @-prefixed placeholders embedded in the SQL statement Configuring the SqlCommand instance as a stored procedure with associated
pa-rameters also works
C#
// - Call the GetCustomerOrders stored procedure with a
// single 'customer ID' argument.
string sqlText = "dbo.GetOrdersForCustomer";
SqlCommand commandWrapper = new SqlCommand(sqlText, linkToDB);
commandWrapper.CommandType = CommandType.StoredProcedure;
commandWrapper.Parameters.AddWithValue("@customerID", ActiveCustomerID);
// - Retrieve the data.
SqlDataAdapter workAdapter = new SqlDataAdapter(commandWrapper);
DataTable orders = new DataTable();
workAdapter.Fill(orders);
Visual Basic
' - Call the GetCustomerOrders stored procedure with a
' single 'customer ID' argument.
Dim sqlText As String = "dbo.GetOrdersForCustomer"
Dim commandWrapper As New SqlCommand(sqlText, linkToDB)
commandWrapper.CommandType = CommandType.StoredProcedure
commandWrapper.Parameters.AddWithValue("@customerID", ActiveCustomerID)
' - Retrieve the data.
Dim workAdapter As New SqlDataAdapter(commandWrapper)
Dim orders As New DataTable
workAdapter.Fill(orders)
Moving Data into a DataSet
Moving external data into a waiting DataSet instance is as easy as filling a DataTable To im-port the data into a DataSet, call the SqlDataAdapter.Fill method, passing it an instance of DataSet.
Trang 9174 Microsoft ADO NET 4 Step by Step
C#
DataSet targetSet = new DataSet();
SqlDataAdapter workAdapter = new SqlDataAdapter(
"SELECT * FROM Customer ORDER BY LastName", connectionString);
workAdapter.Fill(targetSet);
Visual Basic
Dim targetSet As New DataSet
Dim workAdapter As New SqlDataAdapter(
"SELECT * FROM Customer ORDER BY LastName", connectionString)
workAdapter.Fill(targetSet)
As with a DataTable load, the DataSet version of Fill will auto-build the schema for you If you want to preconfigure the DataSet schema, you can build its table by hand or call the SqlDataAdapter.FillSchema method just before you call the Fill method.
C#
// - First build the schema using the structure defined
// in the data source.
workAdapter.FillSchema(targetSet, SchemaType.Source);
// - Then load the data.
workAdapter.Fill(targetSet);
Visual Basic
' - First build the schema using the structure defined
' in the data source.
workAdapter.FillSchema(targetSet, SchemaType.Source)
' - Then load the data.
workAdapter.Fill(targetSet)
Note Passing SchemaType.Mapped as the second argument to FillSchema enables a “mapped”
schema build Schema mapping is discussed on page 186 in the “Table and Column Mapping” section of this chapter.
Fill names the first created table in the data set “Table,” as is done when filling a DataTable directly To alter this default name, specify the new name as a second argument to the Fill
method
Trang 10Chapter 11 Making External Data Available Locally 175 C#
workAdapter.Fill(targetSet, "Customer");
Visual Basic
workAdapter.Fill(targetSet, "Customer")
The Fill(DataSet) method will import multiple tables if its SelectCommand includes a batch
of SELECT statements or a stored procedure that returns multiple result sets The first table
created is still named “Table” (by default) Subsequent tables are named numerically, with the second table given the name “Table1,” the third table “Table2,” and so on Duplicate column names found in any table are treated the same way The first duplicate column is given a “1” suffix, the second has a “2” suffix, and so on
Note When retrieving multiple tables of data, a call to SqlDataAdapter.FillSchema examines only the schema of the first result set The schemas of subsequent sets can be imported only as a side effect of the Fill method.
Moving Data from Memory to Source
After imported data has been modified within a DataTable (with or without a surrounding DataSet), the same SqlDataAdapter that brought the data in can move the changes back out
to the source Setting up the adapter to accomplish that feat is a little more involved than
just crafting a SELECT statement but still not overwhelmingly difficult Configuring the data
adapter for the return data trip requires setting up the appropriate data manipulation
state-ments and calling the SqlDataAdapter.Update method.
Configuring the Update Commands
The SqlDataAdapter.SelectCommand property manages the movement of data only from the external source to the local DataSet or DataTable To move data in the other direction or delete data, you need to set up three distinct properties: InsertCommand, UpdateCommand, and DeleteCommand Like SelectCommand, these three properties are SqlCommand instances, each containing a SQL statement (or stored procedure), a SqlConnection reference, and parameters Although parameters are optional in the SelectCommand instance, they are an
essential part of the three update commands
The following code sets up selection and data modification properties for a simple table,
UnitOfMeasure, which includes an identity field, ID; and two text fields, ShortName and FullName: