Promethix.Framework.Ado
1.1.0
dotnet add package Promethix.Framework.Ado --version 1.1.0
NuGet\Install-Package Promethix.Framework.Ado -Version 1.1.0
<PackageReference Include="Promethix.Framework.Ado" Version="1.1.0" />
paket add Promethix.Framework.Ado --version 1.1.0
#r "nuget: Promethix.Framework.Ado, 1.1.0"
// Install Promethix.Framework.Ado as a Cake Addin #addin nuget:?package=Promethix.Framework.Ado&version=1.1.0 // Install Promethix.Framework.Ado as a Cake Tool #tool nuget:?package=Promethix.Framework.Ado&version=1.1.0
AdoScope (Official Release)
Recently promoted to v1.0.0 official stable release.
Now incldues .NET 8.0 support
AdoScope offers a simple and flexible solution for managing your ADO.NET connections and transactions. It draws inspiration from the remarkable work in DbContextScope by Mehdime El Gueddari, whose DbContextScope library has been a source of great inspiration for the creation of AdoScope.
While AdoScope is compatible with any ADO.NET provider, it was specifically designed with Dapper in mind. Having extensive experience with Entity Framework and DbContextScope, the goal was to provide a similar solution tailored to the requirements of Dapper.
Unlike Entity Framework, Dapper lacks a DbContext, which can lead to challenges in managing DbConnection and DbTransaction. To address this, AdoScope introduces the concept of an AdoContext—a wrapper around DbConnection and DbTransaction, simplifying their management.
If you are seeking a Unit of Work pattern for Dapper with minimal coding overhead, AdoScope provides an elegant solution.
Supported Features
- Simple and flexible configuration
- Database provider agnostic
- Support for nested transactions
- Support for multiple database connections
- Support for explicit database transactions
- Context specific execution options (transactional, non-transactional)
- Support for specific isolation levels per context and per (explicit) transaction
- Support for multiple databases in a single distributed transaction
- Support for explicit distributed transactions
- Support for asynchronous operations
- Support for default distributed transactions
Future Features
- Support for read only transactions
Release Notes
v1.0.0
Promoted from rc4. Official stable release.
v1.0.0-rc4
Allow for null transaction when "NonTransactional" execution option is used. Prevents exception when using Dapper Query methods.
v1.0.0-rc3
Expose context transaction for compatibility with some Dapper queries. All major features implemented.
v1.0.0-rc2
As a result of targeting for .NET 8 and the latest compiler with existing multi-targeting support for .NET 4.8 - .NET 7, some analyser rule
adjustments have been made. This is to avoid littering the code with lots of #if
statements to enable new syntax.
Please be aware
That when your AdoContext
is configured in transactional mode, it will hold a transaction open until you call
Complete()
or Dispose()
on the AdoScope
, this is by design. If you do not want this behavior, configure your AdoContext
to be non-transactional.
CreateWithTransaction()
forces the creation of a new ambient AdoContext
(i.e. does not join the ambient scope if there is one) and wraps all
AdoContext
instances created within that scope in an explicit database transaction with the provided isolation level.
CreateWithDistributedTransaction()
forces the creation of a new ambient AdoContext
(i.e. does not join the ambient scope if there is one) and wraps all
AdoContext
instances created within that scope in an distributed transaction.
Distributed Transactions will not work at all on .NET 5 or 6. They are supported on .NET 7 or better and only on Windows AFAIK this is a limitation of .NET and it's requirement on OS DTC support e.g. MSDTC. MSDTC will of course need to be enabled and running on Windows as well.
Usage
Install the NuGet package
Install-Package Promethix.Framework.Ado -Version 1.0.0-rc4
Create an ADO Context
public class SqliteContextExample1 : AdoContext
{
public SqliteContextExample1()
{
// No Implementation
}
}
Create a Repository making use of this context
public class SimpleTestRepository : ISimpleTestRepository
{
private readonly IAmbientAdoContextLocator ambientAdoContextLocator;
public SimpleTestRepository(IAmbientAdoContextLocator ambientAdoContextLocator)
{
this.ambientAdoContextLocator = ambientAdoContextLocator;
}
private IDbConnection SqliteConnection => ambientAdoContextLocator.GetContext<SqliteContextExample1>().Connection;
public void Add(TestEntity entity)
{
const string query = "INSERT INTO TestEntity (Name, Description, Quantity) VALUES (@Name, @Description, @Quantity)";
SqliteConnection.Execute(query, entity);
}
public TestEntity GetEntityByName(string name)
{
const string query = "SELECT * FROM TestEntity WHERE Name = @Name";
return SqliteConnection.QuerySingleOrDefault<TestEntity>(query, new { Name = name });
}
}
For Dapper transactions Dapper requires access to the DbTransaction
, if you need access to the transaction you will need to use the
following syntax, the transaction is null when configured as non-transactional and Dapper will accept that. This will allow seamless
configuration of transactional and non-transactional in the AdoScope configuration.
public class SimpleTestRepository : ISimpleTestRepository
{
private readonly IAmbientAdoContextLocator ambientAdoContextLocator;
public SimpleTestRepository(IAmbientAdoContextLocator ambientAdoContextLocator)
{
this.ambientAdoContextLocator = ambientAdoContextLocator;
}
private IDbConnection SqliteConnection => ambientAdoContextLocator.GetContext<SqliteContextExample1>().Connection;
private IDbTransaction SqliteTransaction => ambientAdoContextLocator.GetContext<SqliteContextExample1>().Transaction;
public void Add(TestEntity entity)
{
const string query = "INSERT INTO TestEntity (Name, Description, Quantity) VALUES (@Name, @Description, @Quantity)";
SqliteConnection.Execute(query, entity, SqliteTransaction);
}
public TestEntity GetEntityByName(string name)
{
const string query = "SELECT * FROM TestEntity WHERE Name = @Name";
return SqliteConnection.QuerySingleOrDefault<TestEntity>(query, new { Name = name }, SqliteTransaction);
}
}
Create a Service making use of this repository and AdoScope.
When the ADO Context is configured with a transactional execution option, this will behave as a Unit of Work. It will commit when Complete() is called.
You can also configure the ADO Context to be non-transactional, in which case it will behave as a simple connection manager, executing queries as they are called.
public void ServiceLayerAddTestEntity()
{
using IAdoScope adoScope = adoScopeFactory.Create();
// Create a test entity
var newTestEntity = new TestEntity { Name = "CreateTest", Description = "Test Description", Quantity = 1 };
// Call our repository to add the entity
simpleTestRepository.Add(newTestEntity);
// Commit the unit of work / transaction (if using ExecutionOption.Transactional)
adoScope.Complete();
}
Configure your DI Container. Just one of many examples of configuration. This example is by hand to show the available options. Recommend you use appsettings.json for configuration. See example below this one for that.
// Still need to register ADO providers you will be using. This is a .NET ADO requirement.
DbProviderFactories.RegisterFactory("Microsoft.Data.Sqlite", SqliteFactory.Instance);
// Register your repositories et al
_ = services.AddSingleton<IAmbientAdoContextLocator, AmbientAdoContextLocator>();
_ = services.AddSingleton<IAdoScopeFactory, AdoScopeFactory>();
_ = services.AddSingleton<IAdoContextGroupFactory, AdoContextGroupFactory>();
_ = services.AddScoped<ISimpleTestRepository, SimpleTestRepository>();
_ = services.AddScoped<IMultiTestRepository, MultiTestRepository>();
// Register your ADO Contexts
var adoContextConfiguration = new AdoContextConfigurationBuilder()
.AddAdoContext<SqliteContextExample1>(options =>
{
_ = options.WithNamedConnection("SqliteContextExample");
_ = options.WithConnectionString("Data Source=mydatabase.db");
_ = options.WithProviderName("Microsoft.Data.Sqlite");
_ = options.WithExecutionOption(AdoContextExecutionOption.Transactional);
_ = options.WithDefaultIsolationLevel(IsolationLevel.ReadCommitted);
})
.Build();
_ = services.AddScoped(provider => adoContextConfiguration);
Recommended Approach
Use appsettings.json for configuration, you can use the following code in your DI composition:
// AdoScope Configuration
var adoScopeConfiguration = new AdoScopeConfigurationBuilder()
.ConfigureScope(options =>
{
_ = options.WithScopeConfiguration(configuration);
})
.Build();
// AdoContexts Configuration
var adoContextConfiguration = new AdoContextConfigurationBuilder()
.AddAdoContext<SqliteContextExample1>(options =>
{
// JSON AdoContext Configuration File Example 1
_ = options.WithNamedContext("SqliteContextExample1", configuration);
})
.AddAdoContext<SqliteContextExample3>(options =>
{
// JSON AdoContext Configuration File Example 3
_ = options.WithNamedContext("SqliteContextExample3", configuration);
})
.Build();
// Register entire AdoScope configuration in DI
_ = services.AddScoped(provider => adoScopeConfiguration);
_ = services.AddScoped(provider => adoContextConfiguration);
For most use cases you want the following configuration:
- Distributed Transactions Off.
- AdoContext Transactions On - provides Unit of Work behaviour.
appsettings.json as follows (see unit test project for more examples):
{
"AdoScopeOptions": {
"ScopeExecutionOption": "Standard",
},
"AdoContextOptions": {
"SqliteContextExample1": {
"ProviderName": "Microsoft.Data.Sqlite",
"ConnectionString": "Data Source=mydatabase.db",
"ExecutionOption": "Transactional"
},
"SqliteContextExample3": {
"ProviderName": "Microsoft.Data.Sqlite",
"ConnectionString": "Data Source=mydatabase3.db",
"ExecutionOption": "Transactional"
}
}
}
For MS SQL Server the Provider Name should be Microsoft.Data.SqlClient
e.g.
DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", SqlClientFactory.Instance);
Product | Versions 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. |
.NET Framework | net48 is compatible. net481 was computed. |
-
.NETFramework 4.8
- Microsoft.Data.SqlClient (>= 5.2.2)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- System.Collections.Immutable (>= 9.0.0)
-
net8.0
- Microsoft.Data.SqlClient (>= 5.2.2)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- System.Collections.Immutable (>= 9.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.
Version | Downloads | Last updated |
---|---|---|
1.1.0 | 92 | 12/3/2024 |
1.1.0-alpha | 70 | 12/2/2024 |
1.0.0 | 129 | 5/2/2024 |
1.0.0-rc4 | 221 | 1/17/2024 |
1.0.0-rc3 | 121 | 1/15/2024 |
1.0.0-rc2 | 191 | 11/19/2023 |
1.0.0-rc1 | 122 | 11/9/2023 |
0.1.42-alpha | 122 | 11/6/2023 |
0.1.39-alpha | 121 | 10/17/2023 |
0.1.32-alpha | 127 | 10/14/2023 |
0.1.30-alpha | 113 | 9/30/2023 |
0.1.29-alpha | 117 | 9/28/2023 |
0.1.28-alpha | 109 | 9/28/2023 |
0.1.24-alpha | 114 | 9/25/2023 |
0.1.20-alpha | 114 | 9/24/2023 |
0.1.7-alpha | 112 | 9/23/2023 |
Support added for implicit/explicit distributed transactions and ambient supression for async workloads.