Raw SQL Queries in .NET

This tutorial explores how to execute raw SQL queries within .NET applications, providing flexibility and control over your database interactions.

Note: While raw SQL offers power, it's crucial to be aware of potential security risks like SQL injection if not handled properly. Always sanitize user input or use parameterized queries.
Introduction to Raw SQL

Object-Relational Mappers (ORMs) like Entity Framework provide high-level abstractions for database operations. However, there are scenarios where direct SQL execution is necessary or more efficient. This might include complex queries, performance optimizations, or leveraging specific database features.

Executing Raw SQL with Entity Framework Core

Entity Framework Core (EF Core) provides straightforward methods to execute raw SQL commands. These methods allow you to pass SQL strings directly to the database provider.

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

For commands that do not return a result set (e.g., INSERT, UPDATE, DELETE), you can use the ExecuteSqlRaw or ExecuteSqlInterpolated methods.

Using ExecuteSqlRaw

ExecuteSqlRaw allows you to pass a SQL string with parameter placeholders.


using (var context = new MyDbContext())
{
    var userId = 1;
    var newEmail = "new.email@example.com";

    // Using parameter placeholders (safer)
    context.Database.ExecuteSqlRaw(
        "UPDATE Users SET Email = {0} WHERE Id = {1}",
        newEmail, userId);
}
        

Using ExecuteSqlInterpolated

ExecuteSqlInterpolated is a more convenient way to embed parameters directly into the interpolated string. EF Core will automatically create parameters for these values, mitigating SQL injection risks.


using (var context = new MyDbContext())
{
    var productId = 101;
    var newPrice = 29.99m;

    // Using interpolated string with parameters
    context.Database.ExecuteSqlInterpolated($"UPDATE Products SET Price = {newPrice} WHERE Id = {productId}");
}
        
Warning: Avoid constructing SQL strings by concatenating user input directly. Always use parameterized queries.

Executing SQL Queries that Return Data (SELECT)

To retrieve data from raw SQL queries, you can use FromSqlRaw or FromSqlInterpolated.

Using FromSqlRaw

This method is used to execute a SQL query and project the results into an entity type.


using (var context = new MyDbContext())
{
    var searchKeyword = "Analytics";

    // Retrieving data with parameters
    var products = context.Products.FromSqlRaw(
        "SELECT * FROM Products WHERE Name LIKE '%' + {0} + '%'",
        searchKeyword);

    foreach (var product in products)
    {
        Console.WriteLine($"Product: {product.Name}, Price: {product.Price}");
    }
}
        

Using FromSqlInterpolated

Similar to ExecuteSqlInterpolated, this method provides a cleaner syntax for embedding parameters.


using (var context = new MyDbContext())
{
    var minPrice = 50.00m;
    var maxPrice = 150.00m;

    // Retrieving data with interpolated parameters
    var expensiveProducts = context.Products.FromSqlInterpolated($@"
        SELECT * FROM Products
        WHERE Price BETWEEN {minPrice} AND {maxPrice}");

    foreach (var product in expensiveProducts)
    {
        Console.WriteLine($"Product: {product.Name}, Price: {product.Price}");
    }
}
        
Tip: When using FromSqlRaw or FromSqlInterpolated, the SQL query *must* return columns that can be mapped to the entity's properties. If you select specific columns, ensure their names match the property names or use aliases.

Executing Raw SQL with Dapper

Dapper is a popular micro-ORM that excels at executing raw SQL with excellent performance. It simplifies the process of mapping query results to C# objects.

Installation

Install Dapper via NuGet Package Manager:


Install-Package Dapper
        

Basic Usage


using System.Data.SqlClient;
using Dapper;

// Assuming you have a connection string
string connectionString = "Your_Connection_String";

using (var connection = new SqlConnection(connectionString))
{
    connection.Open();

    // Querying for a single object
    var user = connection.QuerySingleOrDefault<User>(
        "SELECT Id, Name, Email FROM Users WHERE Id = @UserId",
        new { UserId = 1 });

    if (user != null)
    {
        Console.WriteLine($"User: {user.Name}, Email: {user.Email}");
    }

    // Querying for a list of objects
    var products = connection.Query<Product>(
        "SELECT Id, Name, Price FROM Products WHERE Price > @MinPrice ORDER BY Price DESC",
        new { MinPrice = 100.00m });

    foreach (var product in products)
    {
        Console.WriteLine($"Product: {product.Name}, Price: {product.Price}");
    }

    // Executing non-query
    int affectedRows = connection.Execute(
        "UPDATE Orders SET Status = @NewStatus WHERE OrderId = @Id",
        new { NewStatus = "Shipped", Id = 500 });

    Console.WriteLine($"Rows affected by update: {affectedRows}");
}

// Example User and Product classes (for demonstration)
public class User { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } }
public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } }
        

When to Use Raw SQL

Best Practices for Raw SQL
Note: Raw SQL can sometimes lead to tighter coupling with the database schema. Changes to the schema might require manual updates to your SQL queries.