Promethix.Framework.Ado 1.1.1

dotnet add package Promethix.Framework.Ado --version 1.1.1
                    
NuGet\Install-Package Promethix.Framework.Ado -Version 1.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Promethix.Framework.Ado" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Promethix.Framework.Ado" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="Promethix.Framework.Ado" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Promethix.Framework.Ado --version 1.1.1
                    
#r "nuget: Promethix.Framework.Ado, 1.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=Promethix.Framework.Ado&version=1.1.1
                    
Install Promethix.Framework.Ado as a Cake Addin
#tool nuget:?package=Promethix.Framework.Ado&version=1.1.1
                    
Install Promethix.Framework.Ado as a Cake Tool

AdoScope

Build and Test Published to NuGet


πŸš€ What is AdoScope?

AdoScope is a lightweight and flexible library for Dapper and ADO.NET that manages DbConnection and DbTransaction lifecycles, while providing clean, scoped transactions through an ambient context pattern.

It provides a minimal-effort Unit of Work pattern, inspired by DbContextScope for Entity Framework β€” but tailored for Dapper and raw ADO.NET.

It has been used in enterprise-scale applications and production systems, giving it a thorough shake-down in complex, high-volume scenarios.

No need to manually manage transactions, connection lifetimes, or implement repetitive unit of work classes. AdoScope wraps all of this with clean Dependency Injection (DI) support and allows:

  • Transparent ambient scoping (no passing around context)
  • Cross-database transaction coordination
  • Distributed transactions (via MSDTC on supported platforms)
  • Fine-grained control per scope or per context

✨ Features

  • βœ… Lightweight ambient scope for ADO.NET
  • βœ… Provider-agnostic (works with MSSQL, SQLite, PostgreSQL, etc.)
  • βœ… Minimal effort Unit of Work via scoped transactions
  • βœ… Nested scope support
  • βœ… Support for multiple databases in the same transaction
  • βœ… Distributed transaction support across databases
  • βœ… Asynchronous-safe usage
  • βœ… Configurable per context and per scope (transactional / non-transactional)
  • βœ… Isolation level configuration per context or scope

πŸ“¦ Installation

Install-Package Promethix.Framework.Ado

πŸ”§ Configuration (Dependency Injection)

.NET with SQLite (Appsettings Example)

DbProviderFactories.RegisterFactory("Microsoft.Data.Sqlite", SqliteFactory.Instance);

services.AddSingleton<IAmbientAdoContextLocator, AmbientAdoContextLocator>();
services.AddSingleton<IAdoScopeFactory, AdoScopeFactory>();
services.AddSingleton<IAdoContextGroupFactory, AdoContextGroupFactory>();

IConfigurationRoot configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build();

var adoScopeConfig = new AdoScopeConfigurationBuilder()
    .ConfigureScope(options => options.WithScopeConfiguration(configuration))
    .Build();

var adoContextConfig = new AdoContextConfigurationBuilder()
    .AddAdoContext<SqliteDbContext>(options =>
        options.WithNamedContext("SqliteDbContext", configuration))
    .Build();

services.AddScoped(_ => adoScopeConfig);
services.AddScoped(_ => adoContextConfig);

.NET with MSSQL (Appsettings Example)

DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", SqlClientFactory.Instance);

services.AddSingleton<IAmbientAdoContextLocator, AmbientAdoContextLocator>();
services.AddSingleton<IAdoScopeFactory, AdoScopeFactory>();
services.AddSingleton<IAdoContextGroupFactory, AdoContextGroupFactory>();

IConfigurationRoot configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build();

var adoScopeConfig = new AdoScopeConfigurationBuilder()
    .ConfigureScope(options => options.WithScopeConfiguration(configuration))
    .Build();

var adoContextConfig = new AdoContextConfigurationBuilder()
    .AddAdoContext<MyDbContext>(options =>
        options.WithNamedContext("MyDbContext", configuration))
    .Build();

services.AddScoped(_ => adoScopeConfig);
services.AddScoped(_ => adoContextConfig);

appsettings.json Example

{
  "AdoScopeOptions": {
    "ScopeExecutionOption": "Standard"
  },
  "AdoContextOptions": {
    "SqliteDbContext": {
      "ProviderName": "Microsoft.Data.Sqlite",
      "ConnectionString": "Data Source=mydatabase.db",
      "ExecutionOption": "Transactional"
    },
    "MyDbContext": {
      "ProviderName": "Microsoft.Data.SqlClient",
      "ConnectionString": "Server=localhost;Database=MyDb;Trusted_Connection=True;",
      "ExecutionOption": "Transactional"
    }
  }
}

.NET Framework Example (Ninject + Fluent Configuration)

// Register AdoScope services
kernel.Bind<IAmbientAdoContextLocator>().To<AmbientAdoContextLocator>().InSingletonScope();
kernel.Bind<IAdoScopeFactory>().To<AdoScopeFactory>().InSingletonScope();
kernel.Bind<IAdoContextGroupFactory>().To<AdoContextGroupFactory>().InSingletonScope();

// Register the ADO.NET provider for MSSQL
DbProviderFactory sqlFactory = DbProviderFactories.GetFactory("Microsoft.Data.SqlClient");
kernel.Bind<DbProviderFactory>().ToConstant(sqlFactory);

// Configure AdoScope globally (application-wide scope behavior)
var adoScopeConfig = new AdoScopeConfigurationBuilder()
    .ConfigureScope(options =>
    {
        options.WithScopeExecutionOption(AdoContextGroupExecutionOption.Standard);
    })
    .Build();

// Configure multiple AdoContexts using fluent API
var adoContextConfig = new AdoContextConfigurationBuilder()
    .AddAdoContext<PrimaryDbContext>(options =>
    {
        options.WithNamedContext("PrimaryDbContext")
            .WithConnectionString(ConfigurationManager.ConnectionStrings["PrimaryDb"].ConnectionString)
            .WithProviderName("Microsoft.Data.SqlClient")
            .WithExecutionOption(AdoContextExecutionOption.Transactional)
            .WithDefaultIsolationLevel(IsolationLevel.ReadCommitted);
    })
    .AddAdoContext<AuditDbContext>(options =>
    {
        options.WithNamedContext("AuditDbContext")
            .WithConnectionString(ConfigurationManager.ConnectionStrings["AuditDb"].ConnectionString)
            .WithProviderName("Microsoft.Data.SqlClient")
            .WithExecutionOption(AdoContextExecutionOption.Transactional)
            .WithDefaultIsolationLevel(IsolationLevel.ReadCommitted);
    })
    .Build();

// Register configurations into DI container
kernel.Bind<AdoScopeOptionsBuilder>().ToConstant(adoScopeConfig).InRequestScope();
kernel.Bind<IAdoContextOptionsRegistry>().ToConstant(adoContextConfig).InRequestScope();


πŸ§ͺ Usage

1. Define Your Context

public class MyDbContext : AdoContext { }
public class SqliteDbContext : AdoContext { }

2. Create a Repository

public class MyRepository : IMyRepository
{
    private readonly IAmbientAdoContextLocator locator;

    public MyRepository(IAmbientAdoContextLocator locator)
    {
        this.locator = locator;
    }

    private IDbConnection Connection => locator.GetContext<MyDbContext>().Connection;
    private IDbTransaction Transaction => locator.GetContext<MyDbContext>().Transaction;

    public int UpdateSomething(string code)
    {
        const string sql = "UPDATE MyTable SET Processed = 1 WHERE Code = @Code";
        return Connection.Execute(sql, new { Code = code }, Transaction);
    }
}

3. Use AdoScope in a Service

public class MyService(IAdoScopeFactory adoScopeFactory, IMyRepository repository) : IMyService
{
    public int ProcessItem(string code)
    {
        using IAdoScope adoScope = adoScopeFactory.Create();
        int affected = repository.UpdateSomething(code);
        adoScope.Complete();
        return affected;
    }
}

4. Unit of Work Style Usage with Multiple Repositories

using IAdoScope scope = adoScopeFactory.Create();

repository1.DoSomething();
repository2.DoSomethingElse();
repository3.BulkInsert(records);
repository4.MarkAsProcessed(ids);

scope.Complete();

⚠️ Notes and Gotchas

  • βœ… You must call .Complete() to commit a transactional scope. If not called, the transaction will automatically roll back on dispose.
  • βœ… If you forget to declare an ambient scope (i.e. no adoScopeFactory.Create()), an explicit and helpful exception will be thrown.
  • βœ… CreateWithTransaction() and CreateWithDistributedTransaction() allow full per-scope control of transaction behavior.
  • βœ… When using Distributed Transactions, ensure:
    • .NET 7+ is used
    • Windows OS with MSDTC service is enabled and running
    • ADO.NET provider supports distributed transactions

πŸ”„ Release Notes

Stable Releases

  • Support for nested scopes, async operations, multiple DBs, and distributed transactions
  • Latest release supports .NET 8

πŸ›£οΈ Roadmap / Future Features

  • Support for read-only transactions
  • Improved exception messages for missing ambient scopes

❀️ Credits

Inspired by Mehdime El Gueddari’s DbContextScope project.


πŸ™Œ Contributing

Pull requests and suggestions welcome! Feel free to open an issue for bugs, ideas, or questions.


πŸ“„ License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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. 
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.1 147 4/7/2025
1.1.0 144 12/3/2024
1.1.0-alpha 79 12/2/2024
1.0.0 145 5/2/2024
1.0.0-rc4 224 1/17/2024
1.0.0-rc3 123 1/15/2024
1.0.0-rc2 193 11/19/2023
1.0.0-rc1 125 11/9/2023
0.1.42-alpha 130 11/6/2023
0.1.39-alpha 123 10/17/2023
0.1.32-alpha 129 10/14/2023
0.1.30-alpha 115 9/30/2023
0.1.29-alpha 119 9/28/2023
0.1.28-alpha 111 9/28/2023
0.1.24-alpha 117 9/25/2023
0.1.20-alpha 116 9/24/2023
0.1.7-alpha 116 9/23/2023

Support added for implicit/explicit distributed transactions and ambient supression for async workloads.