ASP.NET Core Tutorials

Accessing Data with ADO.NET

Introduction to ADO.NET in ASP.NET Core

ADO.NET is a foundational set of .NET classes that provide access to data sources such as SQL Server and XML. It is a technology that allows developers to write code that interacts with databases. In ASP.NET Core, you can leverage ADO.NET for direct data access, offering a low-level and efficient way to manage database operations.

While Entity Framework Core (EF Core) provides a higher-level abstraction for data access, understanding ADO.NET is crucial for scenarios requiring fine-grained control over SQL queries, performance optimization, or working with specific database features not fully abstracted by ORMs.

Key ADO.NET Components

The core components of ADO.NET that you'll commonly use include:

Connecting to a Database

Establishing a connection is the first step. You'll need a connection string that specifies the server, database name, and authentication details. It's best practice to store connection strings in your application's configuration, typically in appsettings.json.

Example appsettings.json:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=your_server_name;Database=your_database_name;User Id=your_user;Password=your_password;"
  }
}

In your ASP.NET Core application, you can inject IConfiguration to retrieve this string.

Step 1: Injecting Configuration

In your service or controller, inject IConfiguration:

public class MyService
{
    private readonly IConfiguration _configuration;

    public MyService(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    // ... rest of your service
}

Step 2: Creating a Connection

Use the connection string to create a SqlConnection object. Ensure you dispose of the connection properly using a using statement.

using Microsoft.Data.SqlClient; // For .NET Core, use Microsoft.Data.SqlClient

// ... inside a method

string connectionString = _configuration.GetConnectionString("DefaultConnection");
using (SqlConnection connection = new SqlConnection(connectionString))
{
    // Open the connection
    connection.Open();
    // ... perform database operations
} // Connection is automatically closed and disposed here

Note: For newer .NET Core projects targeting SQL Server, it's recommended to use the Microsoft.Data.SqlClient NuGet package instead of the older System.Data.SqlClient.

Executing Commands

Once connected, you can execute SQL commands using SqlCommand.

Executing a Query with SqlDataReader

This is common for retrieving data.

public List<string> GetProductNames()
{
    var productNames = new List<string>();
    string connectionString = _configuration.GetConnectionString("DefaultConnection");

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        string sql = "SELECT Name FROM Products WHERE IsActive = 1"; // Example query

        using (SqlCommand command = new SqlCommand(sql, connection))
        {
            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    // Safely get string value, handling DBNull
                    string productName = reader["Name"] != DBNull.Value ? reader["Name"].ToString() : string.Empty;
                    productNames.Add(productName);
                }
            }
        }
    }
    return productNames;
}

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

Use ExecuteNonQuery() for commands that don't return a result set.

public int AddProduct(string name, decimal price)
{
    int rowsAffected = 0;
    string connectionString = _configuration.GetConnectionString("DefaultConnection");

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        string sql = "INSERT INTO Products (Name, Price) VALUES (@Name, @Price)";

        using (SqlCommand command = new SqlCommand(sql, connection))
        {
            // Add parameters to prevent SQL injection
            command.Parameters.AddWithValue("@Name", name);
            command.Parameters.AddWithValue("@Price", price);

            rowsAffected = command.ExecuteNonQuery();
        }
    }
    return rowsAffected;
}

Using Parameters to Prevent SQL Injection

It is absolutely critical to use parameters when building SQL queries that include user-supplied input. Never directly concatenate user input into SQL strings.

The example above for AddProduct demonstrates using SqlParameter (via AddWithValue, though explicit type specification is often preferred for clarity and performance).

// More explicit parameter adding:
command.Parameters.Add("@Name", System.Data.SqlDbType.NVarChar, 100).Value = name;
command.Parameters.Add("@Price", System.Data.SqlDbType.Decimal).Value = price;

Executing Stored Procedures

You can also execute stored procedures.

public string GetProductPrice(int productId)
{
    string price = null;
    string connectionString = _configuration.GetConnectionString("DefaultConnection");

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        string storedProcedureName = "GetProductPriceById"; // Assume this SP exists

        using (SqlCommand command = new SqlCommand(storedProcedureName, connection))
        {
            command.CommandType = System.Data.CommandType.StoredProcedure; // Indicate it's an SP

            command.Parameters.AddWithValue("@ProductId", productId);

            using (SqlDataReader reader = command.ExecuteReader())
            {
                if (reader.Read())
                {
                    price = reader["Price"].ToString();
                }
            }
        }
    }
    return price;
}

Advantages of ADO.NET

When to Use ADO.NET vs. EF Core