Microsoft Developer Network
This section delves into more complex scenarios and best practices for using ADO.NET, enabling you to build robust and high-performance data access solutions.
Leveraging asynchronous programming patterns with ADO.NET significantly improves application responsiveness, especially in UI-heavy applications or web services. This involves using methods like ExecuteReaderAsync
, ToListAsync
, and SaveChangesAsync
.
async
and await
.For example, to asynchronously retrieve data:
async Task<List<Product>> GetProductsAsync(string connectionString)
{
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
var command = new SqlCommand("SELECT ProductID, ProductName FROM Products", connection);
var products = new List<Product>();
using (var reader = await command.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
products.Add(new Product
{
ProductID = reader.GetInt32(0),
ProductName = reader.GetString(1)
});
}
}
return products;
}
}
Connection pooling is a crucial optimization technique that minimizes the overhead of establishing database connections. ADO.NET manages connection pools automatically, but understanding how it works can help in troubleshooting and fine-tuning performance.
Ensure your connection strings are consistent to benefit fully from connection pooling. Variations in authentication methods or other parameters can lead to the creation of multiple pools.
Performing database operations one by one can be inefficient due to network latency and command execution overhead. Batching multiple commands into a single request can drastically improve performance.
SqlBulkCopy
for Large Data Transfers:For inserting large amounts of data into SQL Server, SqlBulkCopy
is highly recommended. It offers a much faster alternative to individual INSERT statements.
using (var bulkCopy = new SqlBulkCopy(connectionString))
{
bulkCopy.DestinationTableName = "Products";
bulkCopy.WriteToServer(dataTable); // dataTable is a DataTable containing the data
}
For executing multiple SQL commands (e.g., inserts, updates, deletes) in a single round trip, you can concatenate them with a semicolon and execute them using ExecuteNonQuery
.
string sqlBatch = "INSERT INTO Products (ProductName) VALUES ('Gadget'); INSERT INTO Products (ProductName) VALUES ('Widget');";
using (var command = new SqlCommand(sqlBatch, connection))
{
int rowsAffected = await command.ExecuteNonQueryAsync();
// Note: rowsAffected will be the sum of rows affected by each command.
}
Managing transactions correctly is vital for data integrity, ensuring that a series of operations either all succeed or all fail. Understanding concurrency control helps prevent data corruption in multi-user environments.
System.Transactions
:For distributed transactions or more complex scenarios, the System.Transactions
namespace provides robust transaction management capabilities.
using (var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
using (var connection1 = new SqlConnection(connectionString1))
using (var connection2 = new SqlConnection(connectionString2))
{
// Perform operations on connection1 and connection2
// ...
transactionScope.Complete(); // Commit the transaction
}
} // Transaction is rolled back if Complete() is not called
Adopting specific design patterns can lead to cleaner, more maintainable, and efficient data access code.
Always use using
statements for IDisposable
objects like SqlConnection
, SqlCommand
, and SqlDataReader
to ensure resources are properly released.
Storing and retrieving large binary or text data (e.g., images, documents, large text fields) requires specific considerations.
VARBINARY(MAX)
, VARCHAR(MAX)
, or NVARCHAR(MAX)
in SQL Server.SqlDataReader.GetBytes
or SqlDataReader.GetChars
for streaming LOB data to avoid loading the entire content into memory at once.