DataAdapter Objects
The DataAdapter
object serves as a bridge between a DataSet
and a data source for retrieving and saving data. It provides a way to fill a DataSet
with data from a data source and to send changes made to the DataSet
back to the data source. The DataAdapter
object handles the connection and command logic required to interact with the data source.
Overview
A DataAdapter
abstracts the details of data retrieval and persistence. It uses four key components to interact with the data source:
SelectCommand
: ACommand
object that retrieves records from the data source.InsertCommand
: ACommand
object that inserts new records into the data source.UpdateCommand
: ACommand
object that updates existing records in the data source.DeleteCommand
: ACommand
object that deletes records from the data source.
When you call methods like Fill()
on a DataAdapter
, it uses the SelectCommand
to populate a DataSet
. When you call Update()
, it examines the RowState
of the rows in a DataTable
within the DataSet
and executes the appropriate command (InsertCommand
, UpdateCommand
, or DeleteCommand
) for each modified row.
Key DataAdapter Classes
ADO.NET provides specific implementations of the DataAdapter
interface for different data providers:
SqlDataAdapter
: Used with SQL Server.OracleDataAdapter
: Used with Oracle databases.OleDbDataAdapter
: Used with OLE DB data sources (which can include various databases like Access, Excel, or even SQL Server via OLE DB providers).OdbcDataAdapter
: Used with ODBC data sources.
Using the DataAdapter
Here's a typical workflow for using a DataAdapter
:
- Create a
Connection
object to connect to the data source. - Create a
Command
object for selecting data, defining the SQL query. - Create a
DataAdapter
object (e.g.,SqlDataAdapter
) and set itsSelectCommand
property. - Create a
DataSet
object to hold the data. - Call the
Fill()
method of theDataAdapter
, passing theDataSet
and the name of the table to populate. - Optionally, modify the data in the
DataSet
. - If changes are made, create
Command
objects for inserting, updating, and deleting data, and assign them to theInsertCommand
,UpdateCommand
, andDeleteCommand
properties of theDataAdapter
, respectively. - Call the
Update()
method of theDataAdapter
, passing theDataSet
.
Example:
using System;
using System.Data;
using System.Data.SqlClient;
public class DataAdapterExample
{
public static void Main(string[] args)
{
string connectionString = "Your_Connection_String_Here";
string selectSql = "SELECT CustomerID, CompanyName FROM Customers";
string insertSql = "INSERT INTO Customers (CompanyName) VALUES (@CompanyName); SELECT CAST(SCOPE_IDENTITY() AS INT);";
string updateSql = "UPDATE Customers SET CompanyName = @CompanyName WHERE CustomerID = @CustomerID";
string deleteSql = "DELETE FROM Customers WHERE CustomerID = @CustomerID";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlDataAdapter adapter = new SqlDataAdapter();
// Select Command
adapter.SelectCommand = new SqlCommand(selectSql, connection);
// Insert Command
adapter.InsertCommand = new SqlCommand(insertSql, connection);
adapter.InsertCommand.Parameters.Add("@CompanyName", SqlDbType.NVarChar, 100, "CompanyName");
// For InsertCommand, we often need to capture the generated ID.
// This example assumes SQL Server's SCOPE_IDENTITY() for auto-generated keys.
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
// Update Command
adapter.UpdateCommand = new SqlCommand(updateSql, connection);
adapter.UpdateCommand.Parameters.Add("@CompanyName", SqlDbType.NVarChar, 100, "CompanyName");
adapter.UpdateCommand.Parameters.Add("@CustomerID", SqlDbType.Int, 0, "CustomerID");
// Delete Command
adapter.DeleteCommand = new SqlCommand(deleteSql, connection);
adapter.DeleteCommand.Parameters.Add("@CustomerID", SqlDbType.Int, 0, "CustomerID");
DataSet dataSet = new DataSet();
try
{
connection.Open();
adapter.Fill(dataSet, "Customers"); // Fill the DataSet
Console.WriteLine("--- Original Data ---");
foreach (DataRow row in dataSet.Tables["Customers"].Rows)
{
Console.WriteLine($"ID: {row["CustomerID"]}, Name: {row["CompanyName"]}");
}
// --- Example of modifying data ---
Console.WriteLine("\n--- Modifying Data ---");
// Add a new row
DataRow newRow = dataSet.Tables["Customers"].NewRow();
newRow["CompanyName"] = "New Tech Solutions";
dataSet.Tables["Customers"].Rows.Add(newRow);
Console.WriteLine("Added new row.");
// Update an existing row
if (dataSet.Tables["Customers"].Rows.Count > 0)
{
DataRow firstRow = dataSet.Tables["Customers"].Rows[0];
firstRow["CompanyName"] = firstRow["CompanyName"] + " (Updated)";
Console.WriteLine($"Updated row with ID: {firstRow["CustomerID"]}");
}
// --- Update the data source ---
Console.WriteLine("\n--- Updating Data Source ---");
int rowsAffected = adapter.Update(dataSet, "Customers");
Console.WriteLine($"Rows affected by update: {rowsAffected}");
// --- Refresh data to show changes ---
Console.WriteLine("\n--- Data After Update ---");
dataSet.Clear(); // Clear the existing dataset to re-fetch
adapter.Fill(dataSet, "Customers");
foreach (DataRow row in dataSet.Tables["Customers"].Rows)
{
Console.WriteLine($"ID: {row["CustomerID"]}, Name: {row["CompanyName"]}");
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}
}
Handling Concurrency
When multiple users might be modifying the same data, concurrency issues can arise. The DataAdapter
can be configured to detect and handle these situations. By default, it performs an optimistic concurrency check:
- Before an update or delete operation, the
DataAdapter
checks if the original values of the row in the database match the original values stored in theDataSet
. - If they don't match, it means another user has modified the row, and an exception (typically
DBConcurrencyException
) is thrown.
You can customize how concurrency conflicts are handled by implementing event handlers for the RowUpdating
and RowUpdating
events of the DataAdapter
.
Key Methods and Properties
Commonly used members of the DataAdapter
class:
Fill(DataSet)
: Populates aDataSet
with the results of executing theSelectCommand
.Fill(DataTable)
: Populates a specificDataTable
within aDataSet
.Fill(DataSet, string)
: Populates aDataSet
and names the resulting table.Update(DataSet)
: Sends changes made to aDataSet
back to the data source.Update(DataTable)
: Sends changes made to a specificDataTable
back to the data source.SelectCommand
: Gets or sets theCommand
used to retrieve records from the data source.InsertCommand
: Gets or sets theCommand
used to insert new records into the data source.UpdateCommand
: Gets or sets theCommand
used to update existing records in the data source.DeleteCommand
: Gets or sets theCommand
used to delete records from the data source.