Skip to content

Integration Dapper Extension

aryehcitron@gmail.com edited this page May 24, 2026 · 15 revisions

The Kronikol.Extensions.Dapper package adds Dapper and ADO.NET SQL operation tracking to your test diagrams. Instead of seeing no database activity in your diagrams, your sequence diagrams show classified SQL operations like SELECT FROM Users or INSERT INTO Orders with a sql:// URI scheme.

This extension has zero Dapper-specific dependencies — it works with any ADO.NET provider (SQL Server, PostgreSQL, SQLite, MySQL, etc.) by wrapping DbConnection.

Using a shared library or abstraction layer? If your code doesn't use DbConnection/Dapper directly — e.g. it goes through a shared repository library, ORM wrapper, or custom abstraction — this extension won't be able to intercept the underlying calls. If EF Core is your ORM, use the EF Core Extension instead. For other cases, see Tracking Custom Dependencies for alternative approaches including RequestResponseLogger.LogPair() and TrackingProxy<T>.


How It Works

TrackingDbConnection is a DbConnection decorator that wraps your real connection. When Dapper (or any ADO.NET code) creates a command through the connection, it returns a TrackingDbCommand that intercepts ExecuteReader, ExecuteNonQuery, and ExecuteScalar (both sync and async). Each intercepted execution is classified by DapperOperationClassifier using regex-based SQL parsing, then logged to RequestResponseLogger.

Because it logs to the same RequestResponseLogger as the standard TestTrackingMessageHandler, SQL operations appear alongside your HTTP API calls in the same sequence diagram — showing the complete flow from test → API → Database.


Install

dotnet add package Kronikol.Extensions.Dapper

Verbosity Levels

Level Method shown URI shown SQL text Parameters
Raw Full SQL text sql://dataSource/database Yes If LogParameters=true
Detailed SELECT FROM Users, INSERT INTO Orders sql://dataSource/database/table If LogSqlText=true If LogParameters=true
Summarised SELECT, INSERT, DELETE sql:///database/table No No

The default is Detailed.

Diagram Label Examples

SQL Raw Detailed Summarised
SELECT * FROM Users WHERE Id = @id SELECT * FROM Users WHERE Id = @id SELECT FROM Users SELECT
INSERT INTO Orders (Name) VALUES (@name) INSERT INTO Orders (Name) VALUES (@name) INSERT INTO Orders INSERT
UPDATE Products SET Price = @p WHERE Id = @id UPDATE Products SET Price = @p WHERE Id = @id UPDATE Products UPDATE
DELETE FROM Sessions WHERE Expired = 1 DELETE FROM Sessions WHERE Expired = 1 DELETE FROM Sessions DELETE
EXEC sp_GetUserDetails @userId EXEC sp_GetUserDetails @userId EXEC sp_GetUserDetails EXEC
CREATE TABLE Temp (Id INT) CREATE TABLE Temp (Id INT) CreateTable CreateTable

Classified Operations

The classifier recognises these SQL operations from command text:

Operation SQL Pattern
Query SELECT ... (extracts table from FROM clause)
Insert INSERT INTO ... (extracts table name)
Update UPDATE ... (extracts table name)
Delete DELETE FROM ... (extracts table name)
Merge MERGE ...
StoredProcedure EXEC ... or CommandType.StoredProcedure
CreateTable CREATE TABLE ...
AlterTable ALTER TABLE ...
DropTable DROP TABLE ...
CreateIndex CREATE INDEX ...
Truncate TRUNCATE ...
BeginTransaction BEGIN TRAN...
Commit COMMIT ...
Rollback ROLLBACK ...
Other Unrecognised SQL

Stored procedures are also detected when CommandType.StoredProcedure is set on the command, regardless of the SQL text.


Setup

Option A: DI Decoration (Recommended — Zero Prod Changes)

v2.27.14+ Use the built-in DI extension method. It automatically resolves IHttpContextAccessor from DI and handles the dual-resolution test identity pattern.

// In your test's ConfigureTestServices:
services.AddDapperTestTracking(options =>
{
    options.ServiceName = "UserDB";
    options.CallerName = "My API";
    options.Verbosity = DapperTrackingVerbosity.Detailed;
    options.CurrentTestInfoFetcher = CurrentTestInfo.Fetcher;
});

This uses DecorateAll<DbConnection> internally — it finds all existing DbConnection registrations, replaces each with a TrackingDbConnection wrapper, and preserves the original service lifetime. No-op if no DbConnection registrations exist. No changes to production code required.

Option B: Extension Method

var trackingOptions = new DapperTrackingOptions
{
    ServiceName = "UserDB",
    CallerName = "My API",
    Verbosity = DapperTrackingVerbosity.Detailed,
    CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
};

// Wrap any DbConnection
using var connection = new SqlConnection(connectionString)
    .WithTestTracking(trackingOptions);

// Use with Dapper as normal
var users = await connection.QueryAsync<User>("SELECT * FROM Users");

Option C: Direct Construction

using var inner = new SqlConnection(connectionString);
using var connection = new TrackingDbConnection(inner, trackingOptions);

// All Dapper/ADO.NET calls through this connection are tracked
var user = await connection.QuerySingleAsync<User>(
    "SELECT * FROM Users WHERE Id = @Id",
    new { Id = 42 });

Option D: Manual DI Registration

builder.ConfigureTestServices(services =>
{
    services.AddScoped<DbConnection>(sp =>
    {
        var trackingOptions = new DapperTrackingOptions
        {
            ServiceName = "Database",
            CallerName = "My API",
            Verbosity = DapperTrackingVerbosity.Detailed,
            CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
        };

        var inner = new SqlConnection(connectionString);
        return inner.WithTestTracking(trackingOptions);
    });
});

Transaction Tracking

TrackingDbConnection automatically wraps transactions in TrackingDbTransaction, which logs BEGIN, COMMIT, and ROLLBACK operations:

using var connection = new SqlConnection(connectionString)
    .WithTestTracking(trackingOptions);
await connection.OpenAsync();

using var transaction = connection.BeginTransaction();
await connection.ExecuteAsync("INSERT INTO Orders ...", param, transaction);
transaction.Commit(); // Logged as "Commit"

Configuration Reference

DapperTrackingOptions

Property Type Default Description
ServiceName string "Database" The participant name shown in the diagram for the database
CallerName string "Caller" The participant name shown for the service making database calls
Verbosity DapperTrackingVerbosity Detailed Controls diagram detail level (Raw, Detailed, Summarised)
CurrentTestInfoFetcher Func<(string Name, string Id)?>? null Returns the current test's name and ID. Required — if null, commands execute normally but are not logged
CurrentStepTypeFetcher Func<string?>? null Optional — returns the current BDD step type (Given/When/Then)
LogParameters bool false Whether to include command parameters in the logged content
LogSqlText bool true Whether to include the full SQL text in the logged content (Detailed mode)
ExcludedOperations HashSet<DapperOperation> [] Operations to skip logging for (e.g., exclude BeginTransaction/Commit)
SetupVerbosity DapperTrackingVerbosity? null Verbosity override for the Setup phase. See Phase-Aware Tracking
ActionVerbosity DapperTrackingVerbosity? null Verbosity override for the Action phase. See Phase-Aware Tracking
HttpContextAccessor IHttpContextAccessor? null Optional — enables dual-resolution of test identity from HTTP headers. Auto-resolved by DI extensions (v2.26.3+). See HTTP Tracking Setup#Dual-Resolution Test Identity (v2.23.0+)

v2.23.0+ Dual-Resolution: TrackingDbConnection accepts an optional IHttpContextAccessor? httpContextAccessor constructor parameter for resolving test identity from HTTP request headers when running inside the SUT's request pipeline. v2.26.3+: Set HttpContextAccessor on DapperTrackingOptions instead — the tracker reads it automatically. See HTTP Tracking Setup#Dual-Resolution Test Identity (v2.23.0+) for details. | TrackDuringSetup | bool | true | When false, tracking is suppressed during Setup. See Phase-Aware Tracking | | TrackDuringAction | bool | true | When false, tracking is suppressed during Action. See Phase-Aware Tracking | | LogResponseContent | bool | true | Include response data in diagram response arrows at all verbosity levels (v2.37.3+). Set to false to restore previous empty-arrow behaviour | | MaxResponseRows | int | 10 | Maximum rows to capture in response content | | MaxValueDisplayLength | int | 500 | Truncate individual cell values beyond this length | | ResponseDetail | SqlResponseDetail | RowCountAndColumns | Level of detail for response content: RowCountOnly, RowCountAndColumns, or FullRows |

CurrentTestInfoFetcher by Framework

Every framework package provides a CurrentTestInfo static class with a Fetcher property. The syntax is identical regardless of framework - just ensure you have the correct using directive:

CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
Framework using directive
xUnit v3 using Kronikol.xUnit3;
xUnit v2 using Kronikol.xUnit2;
NUnit 4 using Kronikol.NUnit4;
MSTest using Kronikol.MSTest;
TUnit using Kronikol.TUnit;
LightBDD using Kronikol.LightBDD;
ReqNRoll using Kronikol.ReqNRoll;
BDDfy using Kronikol.BDDfy.xUnit3;

Extension Methods

DbConnection.WithTestTracking()

public static TrackingDbConnection WithTestTracking(
    this DbConnection connection,
    DapperTrackingOptions options)

Wraps any DbConnection in a TrackingDbConnection. All commands created through this connection will be intercepted and logged.


Response Payload Capture (v2.37.0+)

Response arrows in diagrams now show actual data instead of being empty. The ResponseDetail option controls the level of detail:

ResponseDetail Example output
RowCountOnly 3 rows
RowCountAndColumns (default) 3 rows [Name, Preference, CreatedAt]
FullRows JSON row preview (up to MaxResponseRows)

Response content is shown at all verbosity levels when LogResponseContent = true (the default). Set LogResponseContent = false to restore previous empty-arrow behaviour.

Tuning Response Output

new DapperTrackingOptions
{
    Verbosity = DapperTrackingVerbosity.Summarised,
    LogResponseContent = true,                          // default — show response data
    ResponseDetail = SqlResponseDetail.RowCountOnly,    // just "3 rows" instead of column names
    MaxResponseRows = 10,                               // capture up to 10 rows (default: 10)
    MaxValueDisplayLength = 200                          // truncate cell values at 200 chars (default: 500)
}

Dapper vs EF Core Tracking

Dapper Extension EF Core Extension
Package Extensions.Dapper Extensions.EfCore.Relational
Mechanism DbConnection decorator EF Core IInterceptor
SQL Classification Regex on command text Same
Setup connection.WithTestTracking() AddSqlTestTracking()
Dependencies None (pure ADO.NET) Microsoft.EntityFrameworkCore.Relational
Use When Dapper, raw ADO.NET, micro-ORMs Entity Framework Core

Invocation Validation

TrackingDbConnection implements ITrackingComponent and auto-registers with TrackingComponentRegistry on construction. At report generation time, unused components are automatically detected and surfaced as console warnings and in the diagnostic report (when DiagnosticMode=true). This never throws or fails tests.

See Diagnostics and Debugging for full details on the TrackingComponentRegistry API.

Home


Demo


Getting Started

Common Tasks

Integration Guides

Extensions

Configuration

Features

Reference

Clone this wiki locally