Tải bản đầy đủ (.pdf) (22 trang)

Making External Data Available Locally

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (435.94 KB, 22 trang )

169
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.
Dwonloaded from: iDATA.ws


170
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
Mapping
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.
Dwonloaded from: iDATA.ws
Chapter 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.
Dwonloaded from: iDATA.ws
172
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 connection 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.
Dwonloaded from: iDATA.ws
Chapter 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.
Dwonloaded from: iDATA.ws
174
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.
Dwonloaded from: iDATA.ws
Chapter 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:
Dwonloaded from: iDATA.ws
176
Microsoft ADO.NET 4 Step by Step
C#
// ----- Build the selection query.
SqlDataAdapter unitAdapter = new SqlDataAdapter();
SqlCommand unitCommand = new SqlCommand(
"SELECT * FROM UnitOfMeasure", linkToDB);
unitAdapter.SelectCommand = unitCommand;
// ----- Build the insertion query.
unitCommand = new SqlCommand(
@"INSERT INTO UnitOfMeasure (ShortName, FullName)

VALUES (@ShortName, @FullName); SET @ID = @@IDENTITY;", linkToDB);
unitCommand.Parameters.Add("@ShortName", SqlDbType.VarChar, 15, "ShortName");
unitCommand.Parameters.Add("@FullName", SqlDbType.VarChar, 50, "FullName");
SqlParameter param =
unitCommand.Parameters.Add("@ID", SqlDbType.BigInt, 0, "ID");
param.Direction = ParameterDirection.Output;
unitAdapter.InsertCommand = unitCommand;
// ----- Build the revision query.
unitCommand = new SqlCommand(
@"UPDATE UnitOfMeasure SET ShortName = @ShortName,
FullName = @FullName WHERE ID = @ID", linkToDB);
unitCommand.Parameters.Add("@ShortName", SqlDbType.VarChar, 15, "ShortName");
unitCommand.Parameters.Add("@FullName", SqlDbType.VarChar, 50, "FullName");
param = unitCommand.Parameters.Add("@ID", SqlDbType.BigInt, 0, "ID");
param.SourceVersion = DataRowVersion.Original;
unitAdapter.UpdateCommand = unitCommand;
// ----- Build the deletion query.
unitCommand = new SqlCommand(
"DELETE FROM UnitOfMeasure WHERE ID = @ID", linkToDB);
param = unitCommand.Parameters.Add("@ID", SqlDbType.BigInt, 0, "ID");
param.SourceVersion = DataRowVersion.Original;
unitAdapter.DeleteCommand = unitCommand;
Dwonloaded from: iDATA.ws

×