AdoScope – Scoped Unit of Work Pattern for Dapper & ADO.NET
I’ve published a small NuGet package called AdoScope that brings a scoped Unit of Work pattern to Dapper and ADO.NET, without the need to hand-roll your own classes.
It’s heavily inspired by the excellent DbContextScope
from the EF world (which I’ve used many times), but designed for raw ADO.NET and Dapper.
AdoScope takes care of DbConnection
and DbTransaction
management behind the scenes, so there’s no need to pass transactions around or write boilerplate Unit of Work code, just clean, focused repository logic.
It’s already in use across several enterprise applications, so it’s had a solid shake-down in real-world, high-volume scenarios. It’s not doing anything particularly exotic under the hood either.
Typical usage looks like this:
using var scope = adoScopeFactory.Create();
repository.Add(entity);
repository.MarkAsProcessed(id);
scope.Complete(); // Commits the transaction
Key features:
- Scoped transaction and connection management
- Minimal setup for Unit of Work in Dapper
- Ambient context pattern — no passing around state
- Configurable via appsettings or fluent registration
- Works with SQL Server, SQLite, etc.
- Optional support for multi-DB and distributed transactions (MSDTC on .NET 7+)
- MIT Licence
📦 Promethix.Framework.Ado on NuGet
📖 GitHub README
The ambient context pattern can sometimes be seen as an anti-pattern, especially if it hides state or creates unclear control flow. In the case of AdoScope, the scope is created explicitly and flows naturally within the async execution context. It’s clean, bounded, and easy to reason about, not global or leaky.
This approach helps avoid the pain of passing DbTransaction
objects through multiple layers or creating custom Unit of Work classes to wire everything together. Alternatives like TransactionScope
can be heavier and still ambient under the hood, while AdoScope remains lightweight and flexible, using DbTransaction
wherever possible and unless your use case explicitly requires TransactionScope
or Distributed Transactions.
Having used DbContextScope
in EF projects, I appreciated how it solved many lifecycle issues like nested transactions and context reuse. Dapper doesn’t need change tracking, but I missed the clean transaction and connection management DbContextScope
offered. AdoScope brings that structure to ADO.NET and Dapper, without the boilerplate!
For a deeper dive into the value of ambient context patterns, I highly recommend Mehdi El Gueddari’s article on managing DbContext in EF, which was a major inspiration for this project.