1. Trang chủ
  2. » Công Nghệ Thông Tin

Making External Data Available Locally

22 218 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Making external data available locally
Thể loại Chapter
Định dạng
Số trang 22
Dung lượng 435,94 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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 instanc

Trang 1

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 2

Database ADO.NET

Customer DataAdapter

SELECT

FillUpdateINSERT

UPDATEDELETE

DataReader Original Data

User UpdatesChanged Data

CommandObjects

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 tations, this chapter’s code samples and examples focus specifically on the SQL Server provider version.

implemen-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 3

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 4

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

Trang 5

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 port the data into a DataSet, call the SqlDataAdapter.Fill method, passing it an instance of

im-DataSet.

Trang 6

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)

// - 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 7

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:

Trang 8

// - 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;

Trang 9

Visual Basic

' - Build the selection query.

Dim unitAdapter As New SqlDataAdapter

Dim unitCommand As 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")

With unitCommand.Parameters.Add("@ID", SqlDbType.BigInt, 0, "ID")

.Direction = ParameterDirection.Output

End With

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")

With unitCommand.Parameters.Add("@ID", SqlDbType.BigInt, 0, "ID")

.SourceVersion = DataRowVersion.Original

End With

unitAdapter.UpdateCommand = unitCommand

' - Build the deletion query.

unitCommand = New SqlCommand(

"DELETE FROM UnitOfMeasure WHERE ID = @ID", linkToDB)

With unitCommand.Parameters.Add("@ID", SqlDbType.BigInt, 0, "ID")

.SourceVersion = DataRowVersion.Original

End With

unitAdapter.DeleteCommand = unitCommand

Trang 10

This code is more complex than the earlier retrieval code, which makes sense given its creased responsibilities Besides the increase in the quantity of code, there are three main enhancements that make this code different from the retrieval-only use of the data adapter.

in-■

Parameter column designation You might have noticed a final column-name

argu-ment added to each of the SqlParameter instances created for use with the @-prefixed

placeholders For example, in the insertion portion of the Visual Basic sample code, the

@ShortName placeholder uses this parameter definition.

unitCommand.Parameters.Add("@ShortName", SqlDbType.VarChar, 15, "ShortName")

The ending “ShortName” argument indicates the name of the column as referenced in

an associated DataTable This allows the three data update commands to associate the parameter with specific columns in the local DataTable version of the content ADO.NET

needs to know this to make data updates at the source possible

Key retrieval on insertion In the example code shown previously, the SQL statement

for the InsertCommand portion of the data adapter is actually a two-statement batch.

INSERT INTO UnitOfMeasure (ShortName, FullName)

VALUES (@ShortName, @FullName);

SET @ID = @@IDENTITY;

The first statement performs the insert of a new record; the second statement retrieves

the primary key of the new record, a column tied to a SQL Server IDENTITY constraint The goal is to retrieve the new record identifier so that the local DataTable copy of the record can be properly refreshed with this ID The associated SqlParameter instance for the @ID placeholder has its Direction property set to Output, as shown in the following

C# code line:

param.Direction = ParameterDirection.Output;

As long as the parameter is configured to retrieve the key value, the data adapter will

correctly propagate the new ID value to the DataTable record If you plan to update the data source only once and then immediately destroy the associated DataTable, retriev-

ing the key value is not strictly required But if there is any chance that your code will allow further update and delete operations on the newly inserted record, you will need that ID

Use of original image on update and delete The SQL statement for the

DeleteCommand portion of the code references the record ID as a parameter.

DELETE FROM UnitOfMeasure WHERE ID = @ID

Trang 11

The code adds a SqlParameter instance for the @ID placeholder, shown here as Visual

Basic code:

unitCommand.Parameters.Add("@ID", SqlDbType.BigInt, 0, "ID")

The problem is that by the time the update occurs, the “current” view of the record

in the DataTable has already been deleted There is no current record from which

the adapter can obtain the ID column value To locate the ID, the code must tell the adapter to access the “original” version of the deleted record, using the ID as it existed

when the table was imported or since the last AcceptChanges method call Setting the

SqlParameter.SourceVersion property to DataRowVersion.Original provides that

instruc-tion to the SqlDataAdapter, as shown in this Visual Basic code:

With unitCommand.Parameters.Add("@ID", SqlDbType.BigInt, 0, "ID")

.SourceVersion = DataRowVersion.Original

End With

The UpdateCommand portion includes similar code for cases where the identifying fields may have been modified in the DataTable.

The code shown previously defines the actions the data adapter will perform to move

modi-fied data from the local DataSet or DataTable to the external data store Note that instead

of specific SQL statements, you can define some or all of the four SqlCommand objects tied to the SqlDataAdapter using parameterized stored procedures Whether you use SQL

statements or SQL Server stored procedures to modify the external data is up to you The

SqlDataAdapter will work as long as the statements and the linked SqlParameter objects

match up correctly

Performing the Update

With the data modification statements in place, after you have updated records in the local

DataTable copy of your SqlDataAdapter-linked content, you simply call the adapter’s Update

method to move those changes into the external database You must identify which local

source the Update method is to use for the update, which can be either a DataSet (which updates all tables included in that set), a DataTable, or an array of DataRow objects This lets

you manage the granularity of the data you want to send back to external storage

C#

workAdapter.Update(localTable);

Visual Basic

workAdapter.Update(localTable)

Ngày đăng: 03/10/2013, 00:20

TỪ KHÓA LIÊN QUAN