ADO.NET Data Connection and Commands

This document explores the fundamental components of ADO.NET for interacting with data sources: the Connection and Command objects.

The Connection Object

The Connection object represents an open connection to a data source. It is the first step in any data access operation. ADO.NET provides different connection classes for various data providers, such as SqlConnection for SQL Server, OleDbConnection for OLE DB data sources, and OracleConnection for Oracle.

Key Properties and Methods:

Example: Opening a Connection (Conceptual)


using System.Data;
using System.Data.SqlClient; // For SQL Server

// ...

string connectionString = "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;";
SqlConnection connection = null;

try
{
    connection = new SqlConnection(connectionString);
    connection.Open();
    Console.WriteLine("Connection established. State: " + connection.State);
}
catch (SqlException ex)
{
    Console.WriteLine("Error connecting to database: " + ex.Message);
}
finally
{
    if (connection != null && connection.State == ConnectionState.Open)
    {
        connection.Close();
        Console.WriteLine("Connection closed. State: " + connection.State);
    }
}
            
Tip: Always ensure that you close your connections when you are finished with them, or use a using statement to guarantee that resources are released.

The Command Object

The Command object is used to execute commands against a data source. These commands can be SQL statements, stored procedures, or other data manipulation language (DML) statements.

Key Properties and Methods:

Executing Commands:

Commands are typically executed within an open connection context.

Executing a Non-Query (INSERT, UPDATE, DELETE)


using System.Data;
using System.Data.SqlClient;

// ... Assume 'connection' is an already open SqlConnection ...

string sqlInsert = "INSERT INTO Products (ProductName, UnitPrice) VALUES (@Name, @Price)";
using (SqlCommand command = new SqlCommand(sqlInsert, connection))
{
    command.Parameters.AddWithValue("@Name", "New Gadget");
    command.Parameters.AddWithValue("@Price", 19.99);

    try
    {
        int rowsAffected = command.ExecuteNonQuery();
        Console.WriteLine($"Rows affected: {rowsAffected}");
    }
    catch (SqlException ex)
    {
        Console.WriteLine("Error executing command: " + ex.Message);
    }
}
            

Executing a Query with DataReader


using System.Data;
using System.Data.SqlClient;

// ... Assume 'connection' is an already open SqlConnection ...

string sqlSelect = "SELECT ProductID, ProductName, UnitPrice FROM Products WHERE UnitPrice > @MinPrice";
using (SqlCommand command = new SqlCommand(sqlSelect, connection))
{
    command.Parameters.AddWithValue("@MinPrice", 50.00);

    try
    {
        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                Console.WriteLine($"ID: {reader["ProductID"]}, Name: {reader["ProductName"]}, Price: {reader["UnitPrice"]}");
            }
        }
    }
    catch (SqlException ex)
    {
        Console.WriteLine("Error executing query: " + ex.Message);
    }
}
            
Note: Using parameterized queries is crucial for preventing SQL injection vulnerabilities. Always use AddWithValue or specific parameter types rather than concatenating strings into your SQL commands.

Command and Connection Lifecycle Management

Effective management of Connection and Command objects is vital for performance and resource utilization. The using statement in C# is the preferred way to ensure that these disposable objects are properly disposed of, even if exceptions occur.

Best Practice: Using Statement


string connectionString = "Server=myServerAddress;Database=myDataBase;Integrated Security=SSPI;"; // Example with Windows Auth

try
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        Console.WriteLine("Connection opened using 'using'.");

        string sql = "SELECT COUNT(*) FROM Customers";
        using (SqlCommand command = new SqlCommand(sql, connection))
        {
            object result = command.ExecuteScalar();
            Console.WriteLine($"Total customers: {result}");
        } // Command disposed here
    } // Connection closed and disposed here
    Console.WriteLine("Connection closed and disposed.");
}
catch (SqlException ex)
{
    Console.WriteLine("An error occurred: " + ex.Message);
}
            

This approach ensures that the connection is always closed and released, and any associated resources for the command are cleaned up.