Skip to main content

query-with-dapper

Run raw SQL via Dapper inside an ABP repository for hot-path read/aggregate queries while sharing ABP's unit of work, connection, and transaction. USE FOR: complex or slow queries where EF Core's generated SQL is too slow, hand-written SQL you want full control over, deriving from a DbContext-based DapperRepository and using GetDbConnectionAsync/GetDbTransactionAsync to stay in the current unit of work. DO NOT USE FOR: standard CRUD/LINQ/change tracking/migrations (use ef-core-integration), MongoDB queries (use mongodb-integration), unit-of-work configuration itself (use manage-units-of-work), connection string routing (use configure-connection-strings).

Jump to install

Source facts

Repository
abpframework/abp-skills
Last source activity
July 20, 2026 at 03:24
Detected SKILL.md language
English
Stars
18
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
query-with-dapper
description
Run raw SQL via Dapper inside an ABP repository for hot-path read/aggregate queries while sharing ABP's unit of work, connection, and transaction. USE FOR: complex or slow queries where EF Core's generated SQL is too slow, hand-written SQL you want full control over, deriving from a DbContext-based DapperRepository and using GetDbConnectionAsync/GetDbTransactionAsync to stay in the current unit of work. DO NOT USE FOR: standard CRUD/LINQ/change tracking/migrations (use ef-core-integration), MongoDB queries (use mongodb-integration), unit-of-work configuration itself (use manage-units-of-work), connection string routing (use configure-connection-strings).
license
MIT
# Query with Dapper in ABP ABP's `Volo.Abp.Dapper` integration lets you write raw-SQL repositories with [Dapper](https://github.com/DapperLib/Dapper) while staying inside ABP's unit of work. The integration is **built on top of EF Core**: EF Core stays the primary provider and you reach for Dapper only to fine-tune specific queries for maximum performance. ## When to Use - A heavy read or aggregate query where EF Core's generated SQL is too slow. - SQL you want full hand control over, on a hot path. - You want raw SQL that still participates in ABP's unit of work, connection, and transaction. ## When Not to Use - **Standard CRUD, LINQ, change tracking, entities, migrations** — use ef-core-integration; use it for almost everything. - **MongoDB queries** — use mongodb-integration; the Dapper integration requires an EF Core `DbContext`. - **Configuring the unit of work itself** — use manage-units-of-work (this skill only borrows the ambient UoW). - **Connection string / per-tenant database routing setup** — use configure-connection-strings (Dapper inherits routing from the underlying `DbContext`). ## Workflow ### 1. Choose Dapper vs EF Core - **EF Core (default)** — CRUD, change tracking, LINQ, entities, migrations. Use it for almost everything. - **Dapper** — a heavy read or aggregate query where EF Core's generated SQL is too slow, or SQL you want full hand control over. Dapper is a lightweight, high-performance object mapper for these hot paths. You mix the two in the same app. The Dapper integration requires an EF Core `DbContext` — it borrows that context's connection and transaction, so both share the same unit of work. ### 2. Install the package Run in the folder of the `.csproj` you want the package in (the database/EF Core layer in a layered solution): ```bash abp add-package Volo.Abp.Dapper ``` ### 3. Implement a Dapper repository Derive from `DapperRepository<TDbContext>` (namespace `Volo.Abp.Domain.Repositories.Dapper`), where `TDbContext` is your EF Core `DbContext`. Register it for DI. It implements `IUnitOfWorkEnabled`, so ABP makes the current connection/transaction available in method bodies via dynamic proxying (interception). ```csharp using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Dapper; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories.Dapper; using Volo.Abp.EntityFrameworkCore; public class PersonDapperRepository : DapperRepository<MyAppDbContext>, ITransientDependency { public PersonDapperRepository(IDbContextProvider<MyAppDbContext> dbContextProvider) : base(dbContextProvider) { } public virtual async Task<List<string>> GetAllPersonNamesAsync() { var dbConnection = await GetDbConnectionAsync(); return (await dbConnection.QueryAsync<string>( "select Name from People", transaction: await GetDbTransactionAsync()) ).ToList(); } public virtual async Task<int> UpdatePersonNameAsync(Guid id, string name) { var dbConnection = await GetDbConnectionAsync(); // Always scope a write with a WHERE clause — without it this updates every row. return await dbConnection.ExecuteAsync( "update People set Name = @NewName where Id = @Id", new { Id = id, NewName = name }, await GetDbTransactionAsync()); } } ``` ### 4. Preserve the unit of work + connection resolution The base class hands you the ambient connection and transaction that ABP's unit of work is managing — do not open your own connection: ```csharp public virtual async Task<IDbConnection> GetDbConnectionAsync(); // current UoW connection public virtual async Task<IDbTransaction?> GetDbTransactionAsync(); // current UoW transaction (null if none) ``` Because these come from the shared `IDbContextProvider<TDbContext>`: - **Always pass `await GetDbTransactionAsync()`** to your Dapper calls, so raw SQL participates in the same transaction as EF Core writes — otherwise your Dapper query may not see uncommitted changes and won't roll back with the UoW. - Connection resolution (including multi-tenant per-tenant database routing and `[ConnectionStringName]`) is inherited from the underlying `DbContext`, so Dapper hits the same database EF Core would for that context. - The bare `DbConnection` / `DbTransaction` properties exist but are `[Obsolete]` — use the async methods. Rules: - **Make query methods `virtual`.** Interception (which supplies the connection/transaction) only works on virtual members. - The repository resolves its `DbContext` through `IDbContextProvider<TDbContext>`, which must be a configured ABP EF Core `DbContext` (the startup template configures this for you). ### 5. Layered solution tip Define an `IPersonDapperRepository` interface in the domain layer and implement it in the database/EF Core layer. Inject the interface where you need it, keeping the raw SQL out of the domain and application layers. ## Validation - After `abp add-package Volo.Abp.Dapper`, confirm the package reference appears in the target `.csproj`. - Call a Dapper method inside a unit of work that also does EF Core writes; the query should see those uncommitted writes (proving it shares the connection/transaction) and roll back with the UoW. Dapper won't see EF Core's pending changes while they're still in the `ChangeTracker` — flush them first with `autoSave: true` on the write or by calling `SaveChangesAsync()` before the Dapper query. - Confirm query methods are `virtual` — if interception is missing, `GetDbConnectionAsync()`/`GetDbTransactionAsync()` won't resolve the ambient UoW. ## Common Pitfalls - **Opening your own connection** breaks the shared unit of work. Always use `GetDbConnectionAsync()` / `GetDbTransactionAsync()` from the base class. - **Not passing `await GetDbTransactionAsync()`** to Dapper calls means the raw SQL may not see uncommitted EF Core changes and won't roll back with the UoW. - **Non-`virtual` query methods** skip interception, so the ambient connection/transaction is never supplied. - **Using the bare `DbConnection` / `DbTransaction` properties** — they are `[Obsolete]`; use the async methods. ## See also - ABP Dapper integration docs (`framework/data/dapper`) - ABP EF Core integration docs
View on GitHub