Retrieving Data with ADO.NET
ADO.NET provides a rich set of classes for accessing data sources, including relational databases. This section focuses on the fundamental techniques for retrieving data.
Core Components for Data Retrieval
The primary classes involved in retrieving data are:
DbConnection
: Represents a connection to a data source.DbCommand
: Represents a command to execute against a data source.DbDataReader
: Provides a forward-only, read-only stream of data from a data source.DataTable
andDataSet
: In-memory representations of data that can be used in disconnected scenarios.
Using DbDataReader
(Connected Scenario)
The DbDataReader
is the most efficient way to retrieve data when you need to process it row by row without holding the entire dataset in memory. This is known as a connected scenario.
Steps:
- Establish a connection using a
DbConnection
object. - Create a
DbCommand
object with your SQL query. - Associate the command with the connection.
- Execute the command using
ExecuteReader()
, which returns aDbDataReader
. - Iterate through the rows using
Read()
. - Access column data by ordinal index or column name.
- Close the reader and the connection when finished.
Example (SQL Server):
using System;
using System.Data.SqlClient;
public class DataRetriever
{
public void RetrieveCustomerNames(string connectionString)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
string query = "SELECT CustomerID, CompanyName FROM Customers";
SqlCommand command = new SqlCommand(query, connection);
try
{
connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
// Access data by ordinal index
int customerId = reader.GetInt32(0);
string companyName = reader.GetString(1);
Console.WriteLine($"ID: {customerId}, Name: {companyName}");
// Alternatively, access data by column name
// Console.WriteLine($"ID: {reader["CustomerID"]}, Name: {reader["CompanyName"]}");
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
}
try-catch
blocks to handle potential exceptions. Using statement ensures that disposable resources like connections and readers are properly closed and disposed of.
Using DataTable
and DataSet
(Disconnected Scenario)
In a disconnected scenario, you load data into memory using a DataTable
or DataSet
and then process it. This is useful for applications that need to operate on data without maintaining an active connection to the database, such as during UI interactions or when dealing with large datasets.
DataTable
A DataTable
represents a single table of data in memory. You can populate it using a DbDataAdapter
.
DataSet
A DataSet
is a collection of DataTable
objects, along with relationships and constraints between them. It represents a complete in-memory cache of relational data.
Using DataAdapter
and DataTable
The DbDataAdapter
acts as a bridge between a DataSet
(or DataTable
) and a data source. It can fill a DataSet
with data and resolve changes made to the DataSet
back to the data source.
Steps:
- Create a
DbConnection
. - Create a
DbCommand
. - Create a
DbDataAdapter
(e.g.,SqlDataAdapter
). - Create a
DataTable
orDataSet
. - Use the
Fill()
method of the adapter to populate theDataTable
/DataSet
. - Process the data in the
DataTable
/DataSet
. - Close the connection.
Example (SQL Server):
using System;
using System.Data;
using System.Data.SqlClient;
public class DataRetriever
{
public void LoadProductsIntoDataTable(string connectionString)
{
DataTable productsTable = new DataTable("Products");
using (SqlConnection connection = new SqlConnection(connectionString))
{
string query = "SELECT ProductID, ProductName, UnitPrice FROM Products";
SqlDataAdapter adapter = new SqlDataAdapter(query, connection);
try
{
// The adapter automatically opens and closes the connection when Fill is called
adapter.Fill(productsTable);
// Process the data in the DataTable
foreach (DataRow row in productsTable.Rows)
{
Console.WriteLine($"ID: {row["ProductID"]}, Name: {row["ProductName"]}, Price: {row["UnitPrice"]}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
}
DataSet
and defining relations between its DataTable
objects.
Choosing the Right Approach
- Use
DbDataReader
for:- Processing data row-by-row.
- Performance-critical operations where memory usage is a concern.
- Scenarios where the data is needed only for a short duration.
- Use
DataTable
/DataSet
for:- Disconnected applications.
- Working with data in the UI.
- Caching data for later use.
- Handling complex relationships between tables.