Dapper Usage Guide

This tutorial provides a comprehensive guide on how to effectively use Dapper, a lightweight ORM for .NET, to interact with your databases.

Prerequisites

Before you begin, ensure you have the following installed:

Installation

You can install Dapper via NuGet Package Manager. Open your project's Package Manager Console and run:

Install-Package Dapper

Alternatively, you can use the .NET CLI:

dotnet add package Dapper

Establishing a Connection

To interact with your database, you need a connection. Dapper works with any ADO.NET connection object. Here's how to establish a basic SQL Server connection:


using System.Data;
using Microsoft.Data.SqlClient; // Or appropriate provider for your DB

// ...

public IDbConnection GetOpenConnection()
{
    var connectionString = "Server=your_server_name;Database=your_database_name;User Id=your_user_id;Password=your_password;";
    var connection = new SqlConnection(connectionString);
    connection.Open();
    return connection;
}
            

Executing Queries

Querying for a List of Objects

Use the Query<T> method to fetch multiple records and map them to a list of objects.


using Dapper;
using System.Collections.Generic;
using System.Data;
using Microsoft.Data.SqlClient;

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class ProductRepository
{
    public IEnumerable<Product> GetAllProducts()
    {
        using (IDbConnection db = new SqlConnection("YourConnectionString"))
        {
            return db.Query<Product>("SELECT ProductId, Name, Price FROM Products");
        }
    }
}
            

Querying for a Single Object

Use QueryFirstOrDefault<T> or QuerySingleOrDefault<T> to fetch a single record.


public Product GetProductById(int id)
{
    using (IDbConnection db = new SqlConnection("YourConnectionString"))
    {
        return db.QueryFirstOrDefault<Product>("SELECT ProductId, Name, Price FROM Products WHERE ProductId = @Id", new { Id = id });
    }
}
            

Executing Commands (INSERT, UPDATE, DELETE)

Use the Execute method for operations that don't return data.


public int AddProduct(Product product)
{
    using (IDbConnection db = new SqlConnection("YourConnectionString"))
    {
        string sql = "INSERT INTO Products (Name, Price) VALUES (@Name, @Price); SELECT CAST(SCOPE_IDENTITY() as int)";
        return db.Execute(sql, product);
    }
}

public void UpdateProductPrice(int id, decimal newPrice)
{
    using (IDbConnection db = new SqlConnection("YourConnectionString"))
    {
        string sql = "UPDATE Products SET Price = @Price WHERE ProductId = @Id";
        db.Execute(sql, new { Price = newPrice, Id = id });
    }
}

public void DeleteProduct(int id)
{
    using (IDbConnection db = new SqlConnection("YourConnectionString"))
    {
        string sql = "DELETE FROM Products WHERE ProductId = @Id";
        db.Execute(sql, new { Id = id });
    }
}
            

Using Parameters

Dapper automatically handles parameter mapping by matching property names in anonymous objects or specific parameter types to column names or parameter placeholders in your SQL queries. This helps prevent SQL injection vulnerabilities.

Tip: Always use parameterized queries. Avoid string concatenation to build SQL statements.

Executing Stored Procedures

You can execute stored procedures using Query or Execute with the command type set to StoredProcedure.


public IEnumerable<Product> GetProductsByCategory(string categoryName)
{
    using (IDbConnection db = new SqlConnection("YourConnectionString"))
    {
        return db.Query<Product>(
            "GetProductsByCategorySP", // Name of the stored procedure
            new { CategoryName = categoryName },
            commandType: CommandType.StoredProcedure);
    }
}
            

Transactions

Dapper integrates seamlessly with ADO.NET transactions.


public void TransferFunds(int fromAccountId, int toAccountId, decimal amount)
{
    using (IDbConnection db = new SqlConnection("YourConnectionString"))
    {
        db.Open();
        using (var transaction = db.BeginTransaction())
        {
            try
            {
                db.Execute("UPDATE Accounts SET Balance = Balance - @Amount WHERE AccountId = @FromId",
                           new { Amount = amount, FromId = fromAccountId }, transaction: transaction);

                db.Execute("UPDATE Accounts SET Balance = Balance + @Amount WHERE AccountId = @ToId",
                           new { Amount = amount, ToId = toAccountId }, transaction: transaction);

                transaction.Commit();
            }
            catch (Exception)
            {
                transaction.Rollback();
                throw;
            }
        }
    }
}
            

Asynchronous Operations

Dapper provides asynchronous methods for non-blocking database operations.


using Dapper;
using System.Threading.Tasks;

public async Task<IEnumerable<Product>> GetAllProductsAsync()
{
    using (IDbConnection db = new SqlConnection("YourConnectionString"))
    {
        return await db.QueryAsync<Product>("SELECT ProductId, Name, Price FROM Products");
    }
}
            
Note: For asynchronous operations, ensure your project targets a framework version that supports async/await.

Advanced Scenarios

Mapping Complex Types

Dapper can map results to complex types with relationships using split on.


public class Order
{
    public int OrderId { get; set; }
    public DateTime OrderDate { get; set; }
    public List<OrderItem> Items { get; set; } = new List<OrderItem>();
}

public class OrderItem
{
    public int OrderItemId { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
}

public Order GetOrderWithItems(int orderId)
{
    using (IDbConnection db = new SqlConnection("YourConnectionString"))
    {
        var sql = @"
            SELECT o.OrderId, o.OrderDate, oi.OrderItemId, oi.ProductName, oi.Quantity, oi.UnitPrice
            FROM Orders o
            LEFT JOIN OrderItems oi ON o.OrderId = oi.OrderId
            WHERE o.OrderId = @Id;
        ";

        // Use SplitOn to tell Dapper how to separate different types of objects
        var result = db.Query<Order, OrderItem, Order>(
            sql,
            (order, orderItem) =>
            {
                if (orderItem != null)
                {
                    order.Items.Add(orderItem);
                }
                return order;
            },
            splitOn: "OrderItemId", // Column name where the split occurs
            param: new { Id = orderId }
        );

        return result.FirstOrDefault();
    }
}
            

Performance Considerations

Warning: While Dapper is performant, inefficient queries can still lead to performance bottlenecks. Always profile your database interactions.