Dapper.AmbientContext
2.1.0
dotnet add package Dapper.AmbientContext --version 2.1.0
NuGet\Install-Package Dapper.AmbientContext -Version 2.1.0
<PackageReference Include="Dapper.AmbientContext" Version="2.1.0" />
<PackageVersion Include="Dapper.AmbientContext" Version="2.1.0" />
<PackageReference Include="Dapper.AmbientContext" />
paket add Dapper.AmbientContext --version 2.1.0
#r "nuget: Dapper.AmbientContext, 2.1.0"
#:package Dapper.AmbientContext@2.1.0
#addin nuget:?package=Dapper.AmbientContext&version=2.1.0
#tool nuget:?package=Dapper.AmbientContext&version=2.1.0
Dapper.AmbientContext
Ambient context implementation for Dapper with automatic transaction management.
Overview
Dapper.AmbientContext implements the ambient context pattern to seamlessly manage database connections and transactions across multiple data access components. It allows service layers to compose multiple repositories, commands, and queries that automatically participate in the same transaction without explicit connection or transaction passing.
Key Features
- ✅ Automatic transaction management - Share connections and transactions across components
- ✅ Thread-safe - Safe for concurrent operations
- ✅ Nested contexts - Support for parent-child context hierarchies
- ✅ Cross-platform - Works on .NET Framework, .NET Core, and .NET 5+
Installation
Install via NuGet:
dotnet add package Dapper.AmbientContext
Or using Package Manager Console:
Install-Package Dapper.AmbientContext
Requirements
- .NET Framework 4.6.2 or higher
- .NET Core 2.0 or higher
- .NET 5.0 or higher
Note: For .NET Framework 4.5.1 - 4.6.1 support, use version 1.8.1.
Quick Start
1. Set up your connection factory
public class SqlConnectionFactory : IDbConnectionFactory
{
private readonly string _connectionString;
public SqlConnectionFactory(string connectionString)
{
_connectionString = connectionString;
}
public IDbConnection Create()
{
return new SqlConnection(_connectionString);
}
}
2. Register services in your DI container
using Dapper.AmbientContext.Extensions;
// With connection factory instance
services.AddDapperAmbientContext(new SqlConnectionFactory(connectionString));
// OR with connection factory type
services.AddDapperAmbientContext<SqlConnectionFactory>(connectionString);
// Register your repositories
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<IOrderRepository, OrderRepository>();
3. Create a repository
public class UserRepository : IUserRepository
{
private readonly IAmbientDbContextLocator _locator;
public UserRepository(IAmbientDbContextLocator locator)
{
_locator = locator;
}
public async Task<User> GetByIdAsync(int id)
{
var context = _locator.Get();
var prepared = await context.PrepareAsync();
return await prepared.Connection.QuerySingleOrDefaultAsync<User>(
"SELECT * FROM Users WHERE Id = @Id",
new { Id = id },
transaction: prepared.Transaction);
}
public async Task CreateAsync(User user)
{
var context = _locator.Get();
var prepared = await context.PrepareAsync();
await prepared.Connection.ExecuteAsync(
"INSERT INTO Users (Name, Email) VALUES (@Name, @Email)",
user,
transaction: prepared.Transaction);
}
}
4. Use in your service layer
public class UserService
{
private readonly IAmbientDbContextFactory _contextFactory;
private readonly IUserRepository _userRepository;
private readonly IOrderRepository _orderRepository;
public UserService(
IAmbientDbContextFactory contextFactory,
IUserRepository userRepository,
IOrderRepository orderRepository)
{
_contextFactory = contextFactory;
_userRepository = userRepository;
_orderRepository = orderRepository;
}
public async Task<User> CreateUserWithOrderAsync(User user, Order order)
{
// Create ambient context - both repositories will share this transaction
using (var context = _contextFactory.Create())
{
await _userRepository.CreateAsync(user);
await _orderRepository.CreateAsync(order);
// Transaction is automatically committed on Dispose if no exceptions
return user;
}
}
}
Advanced Usage
Nested Contexts
using (var parentContext = _contextFactory.Create())
{
await _userRepository.CreateAsync(user);
// Child context joins parent's transaction
using (var childContext = _contextFactory.Create(join: true))
{
await _orderRepository.CreateAsync(order);
// Child dispose doesn't commit - parent owns the transaction
}
// Transaction committed here when parent is disposed
}
Transaction Suppression
// Create context without a transaction
using (var context = _contextFactory.Create(suppress: true))
{
// Queries run without transaction
var users = await _userRepository.GetAllAsync();
}
Custom Isolation Level
using (var context = _contextFactory.Create(
isolationLevel: IsolationLevel.Serializable))
{
await _userRepository.CreateAsync(user);
}
Manual Commit/Rollback
using (var context = _contextFactory.Create())
{
try
{
await _userRepository.CreateAsync(user);
context.Commit(); // Explicit commit
}
catch
{
context.Rollback(); // Explicit rollback
throw;
}
}
How It Works
The ambient context pattern uses AsyncLocal (.NET Core/5+) or LogicalCallContext (.NET Framework) to maintain a stack of database contexts. When you create a context:
- A new connection is opened (or inherited from parent)
- A transaction is started (unless suppressed)
- Context is pushed onto the ambient stack
- All data access components get the current context via
IAmbientDbContextLocator - On dispose, the context is popped and transaction is committed/rolled back
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
Built on top of Dapper - the simple object mapper for .NET.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.6.2
- Dapper (>= 2.0.151)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.0.0)
- System.Collections.Immutable (>= 8.0.0)
-
.NETStandard 2.0
- Dapper (>= 2.0.151)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.0.0)
- System.Collections.Immutable (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.