Roadmap › Dapper — The Micro-ORM from Basics to Advanced › Module: Dapper in Real Applications
Lessons in this course (8)
7. Repository, unit of work and connection management in ASP.NET Core
Intermediate★ Must knowLesson 7 of 8
csharp
public interface IDbConnectionFactory { DbConnection Create(); }
public sealed class SqlConnectionFactory(IConfiguration cfg) : IDbConnectionFactory
{
public DbConnection Create() => new SqlConnection(cfg.GetConnectionString("Shop"));
}
public interface IProductRepository
{
Task<IReadOnlyList<ProductDto>> SearchAsync(string? q, int page, int size, CancellationToken ct);
Task<ProductDto?> GetAsync(int id, CancellationToken ct);
Task<int> CreateAsync(CreateProduct cmd, CancellationToken ct);
}
public sealed class ProductRepository(IDbConnectionFactory db) : IProductRepository
{
public async Task<IReadOnlyList<ProductDto>> SearchAsync(string? q, int page, int size, CancellationToken ct)
{
const string sql = @"SELECT Id, Name, Category, Price FROM dbo.Products
WHERE (@q IS NULL OR Name LIKE @q + '%')
ORDER BY Name OFFSET @skip ROWS FETCH NEXT @size ROWS ONLY";
await using var conn = db.Create();
var rows = await conn.QueryAsync<ProductDto>(new CommandDefinition(sql, new { q, skip = (page - 1) * size, size }, cancellationToken: ct));
return rows.AsList();
}
public async Task<ProductDto?> GetAsync(int id, CancellationToken ct)
{
await using var conn = db.Create();
return await conn.QuerySingleOrDefaultAsync<ProductDto>(new CommandDefinition(
"SELECT Id, Name, Category, Price FROM dbo.Products WHERE Id = @id", new { id }, cancellationToken: ct));
}
public async Task<int> CreateAsync(CreateProduct c, CancellationToken ct)
{
await using var conn = db.Create();
return await conn.QuerySingleAsync<int>(new CommandDefinition(
"INSERT INTO dbo.Products (Name, Category, Price) OUTPUT INSERTED.Id VALUES (@Name, @Category, @Price)", c, cancellationToken: ct));
}
}
// Program.cs
builder.Services.AddSingleton<IDbConnectionFactory, SqlConnectionFactory>();
builder.Services.AddScoped<IProductRepository, ProductRepository>();- Open connections late and dispose early — ADO.NET connection pooling makes this cheap.
- A unit of work shares one connection + transaction across several repositories for one business operation.
- Keep SQL in one place (repository or
.sqlresources), name queries clearly, and test them against a real database.