From bdbcb9dd234e49b14c04bf342004e3034248bbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Klari=C4=87?= Date: Tue, 31 Mar 2026 09:57:18 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8feat:=20Add=20CancellationToken=20?= =?UTF-8?q?support=20to=20all=20async=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thread CancellationToken through the entire call chain from public interfaces down to Dapper CommandDefinition. All async operations now accept an optional cancellationToken parameter (default: default) and honour cancellation. Adds tests verifying that a pre-cancelled token throws OperationCanceledException. Co-Authored-By: Claude Sonnet 4.6 --- .../Entity/CancellationTokenTests.cs | 173 +++++++++++++ .../DbConnectionExtensions.cs | 63 ++--- src/Simpleverse.Repository.Db/DbRepository.cs | 71 +++--- .../Entity/Entity.cs | 239 ++++++++++-------- .../Entity/ProjectedEntity.cs | 132 +++++----- .../Extensions/TruncateExtensions.cs | 11 +- .../IDbRepository.cs | 7 +- .../Operations/IAddDb.cs | 6 +- .../Operations/IAggregateDb.cs | 9 +- .../Operations/IDeleteDb.cs | 7 +- .../Operations/IQueryExistDb.cs | 3 +- .../Operations/IQueryGetDb.cs | 7 +- .../Operations/IQueryListDb.cs | 13 +- .../Operations/IReplaceDb.cs | 4 +- .../Operations/IUpdateDb.cs | 8 +- .../Operations/IUpsertDb.cs | 5 +- .../Simpleverse.Repository.Db.csproj | 16 +- .../SqlServer/BulkExtensions.cs | 25 +- .../SqlServer/Merge/MergeExtensions.cs | 25 +- .../SqlServer/OutputMapExtensions.cs | 14 +- .../SqlServer/SqlConnectionExtensions.cs | 16 +- .../Entity/ProjectedEntity.cs | 91 +++---- src/Simpleverse.Repository/Operations/IAdd.cs | 5 +- .../Operations/IAggregate.cs | 9 +- .../Operations/IDelete.cs | 7 +- .../Operations/IQueryExist.cs | 3 +- .../Operations/IQueryGet.cs | 5 +- .../Operations/IQueryList.cs | 9 +- .../Operations/IReplace.cs | 4 +- .../Operations/IUpdate.cs | 7 +- .../Operations/IUpsert.cs | 5 +- 31 files changed, 623 insertions(+), 376 deletions(-) create mode 100644 src/Simpleverse.Repository.Db.Test/SqlServer/Entity/CancellationTokenTests.cs diff --git a/src/Simpleverse.Repository.Db.Test/SqlServer/Entity/CancellationTokenTests.cs b/src/Simpleverse.Repository.Db.Test/SqlServer/Entity/CancellationTokenTests.cs new file mode 100644 index 0000000..bd214b2 --- /dev/null +++ b/src/Simpleverse.Repository.Db.Test/SqlServer/Entity/CancellationTokenTests.cs @@ -0,0 +1,173 @@ +using Simpleverse.Repository.Db.Extensions; +using Simpleverse.Repository.Db.SqlServer; +using StackExchange.Profiling.Data; +using System; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace Simpleverse.Repository.Db.Test.SqlServer.Entity +{ + [Collection("SqlServerCollection")] + public class CancellationTokenTests : DatabaseTestFixture + { + private readonly SqlRepository _sqlRepository; + + public CancellationTokenTests(DatabaseFixture fixture, ITestOutputHelper output) + : base(fixture, output) + { + _sqlRepository = new SqlRepository(() => (ProfiledDbConnection)fixture.GetProfiledConnection()); + } + + [Fact] + public async Task ListAsync_WhenCancellationTokenCancelled_ThrowsOperationCanceledException() + { + using (var profiler = Profile()) + using (var connection = _fixture.GetProfiledConnection()) + { + // arrange + connection.Open(); + connection.Truncate(); + var entity = new IdentityEntity(_sqlRepository); + var cancelledToken = new CancellationToken(canceled: true); + + // act & assert + await Assert.ThrowsAnyAsync( + () => entity.ListAsync(cancellationToken: cancelledToken) + ); + } + } + + [Fact] + public async Task GetAsync_WhenCancellationTokenCancelled_ThrowsOperationCanceledException() + { + using (var profiler = Profile()) + using (var connection = _fixture.GetProfiledConnection()) + { + // arrange + connection.Open(); + connection.Truncate(); + var entity = new IdentityEntity(_sqlRepository); + var cancelledToken = new CancellationToken(canceled: true); + + // act & assert + await Assert.ThrowsAnyAsync( + () => entity.GetAsync(cancellationToken: cancelledToken) + ); + } + } + + [Fact] + public async Task AddAsync_WhenCancellationTokenCancelled_ThrowsOperationCanceledException() + { + using (var profiler = Profile()) + using (var connection = _fixture.GetProfiledConnection()) + { + // arrange + connection.Open(); + connection.Truncate(); + var entity = new IdentityEntity(_sqlRepository); + var records = TestData.IdentityWithoutIdData(1); + var cancelledToken = new CancellationToken(canceled: true); + + // act & assert + await Assert.ThrowsAnyAsync( + () => entity.AddAsync(records, cancellationToken: cancelledToken) + ); + } + } + + [Fact] + public async Task UpdateAsync_WhenCancellationTokenCancelled_ThrowsOperationCanceledException() + { + using (var profiler = Profile()) + using (var connection = _fixture.GetProfiledConnection()) + { + // arrange + connection.Open(); + connection.Truncate(); + var entity = new IdentityEntity(_sqlRepository); + var cancelledToken = new CancellationToken(canceled: true); + + // act & assert + await Assert.ThrowsAnyAsync( + () => entity.UpdateAsync( + update => update.Name = "test", + cancellationToken: cancelledToken + ) + ); + } + } + + [Fact] + public async Task DeleteAsync_WhenCancellationTokenCancelled_ThrowsOperationCanceledException() + { + using (var profiler = Profile()) + using (var connection = _fixture.GetProfiledConnection()) + { + // arrange + connection.Open(); + connection.Truncate(); + var entity = new IdentityEntity(_sqlRepository); + var cancelledToken = new CancellationToken(canceled: true); + + // act & assert + await Assert.ThrowsAnyAsync( + () => entity.DeleteAsync(cancellationToken: cancelledToken) + ); + } + } + + [Fact] + public async Task ExistsAsync_WhenCancellationTokenCancelled_ThrowsOperationCanceledException() + { + using (var profiler = Profile()) + using (var connection = _fixture.GetProfiledConnection()) + { + // arrange + connection.Open(); + connection.Truncate(); + var entity = new IdentityEntity(_sqlRepository); + var cancelledToken = new CancellationToken(canceled: true); + + // act & assert + await Assert.ThrowsAnyAsync( + () => entity.ExistsAsync(cancellationToken: cancelledToken) + ); + } + } + + [Fact] + public async Task DbRepository_QueryAsync_WhenCancellationTokenCancelled_ThrowsOperationCanceledException() + { + using (var profiler = Profile()) + { + // arrange + var cancelledToken = new CancellationToken(canceled: true); + + // act & assert + await Assert.ThrowsAnyAsync( + () => _sqlRepository.QueryAsync("SELECT 1", null, cancelledToken) + ); + } + } + + [Fact] + public async Task DbRepository_ExecuteAsync_WhenCancellationTokenCancelled_ThrowsOperationCanceledException() + { + using (var profiler = Profile()) + { + // arrange + var builder = new QueryBuilder(); + var query = builder.AddTemplate("SELECT 1"); + var cancelledToken = new CancellationToken(canceled: true); + + // act & assert + await Assert.ThrowsAnyAsync( + () => _sqlRepository.ExecuteAsync(query, cancelledToken) + ); + } + } + } +} diff --git a/src/Simpleverse.Repository.Db/DbConnectionExtensions.cs b/src/Simpleverse.Repository.Db/DbConnectionExtensions.cs index 1fe3ece..2b039cc 100644 --- a/src/Simpleverse.Repository.Db/DbConnectionExtensions.cs +++ b/src/Simpleverse.Repository.Db/DbConnectionExtensions.cs @@ -1,57 +1,58 @@ -using Dapper; +using Dapper; using System; using System.Collections.Generic; using System.Data; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db { public static class DbConnectionExtensions { - public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null) - => conn.QueryAsync(query.RawSql, param: query.Parameters, transaction: tran); + public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(new CommandDefinition(query.RawSql, query.Parameters, transaction: tran, cancellationToken: cancellationToken)); - public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null) - => conn.QueryAsync(query.RawSql, param: query.Parameters, transaction: tran); + public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(new CommandDefinition(query.RawSql, query.Parameters, transaction: tran, cancellationToken: cancellationToken)); - public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran) - => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran); + public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran, CancellationToken cancellationToken = default) + => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran, cancellationToken: cancellationToken); - public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null) - => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran); + public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran, cancellationToken: cancellationToken); - public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null) - => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran); + public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran, cancellationToken: cancellationToken); - public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null) - => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran); + public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran, cancellationToken: cancellationToken); - public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null) - => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran); + public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran, cancellationToken: cancellationToken); - public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null) - => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran); + public static Task> QueryAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(query.RawSql, param: query.Parameters, tran: tran, cancellationToken: cancellationToken); - public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null) - => conn.QueryAsync(rawSql, (first, second) => (first, second), param: param, transaction: tran); + public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(new CommandDefinition(rawSql, param, transaction: tran, cancellationToken: cancellationToken), (first, second) => (first, second)); - public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null) - => conn.QueryAsync(rawSql, (first, second, third) => (first, second, third), param: param, transaction: tran); + public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(new CommandDefinition(rawSql, param, transaction: tran, cancellationToken: cancellationToken), (first, second, third) => (first, second, third)); - public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null) - => conn.QueryAsync(rawSql, (first, second, third, fourth) => (first, second, third, fourth), param: param, transaction: tran); + public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(new CommandDefinition(rawSql, param, transaction: tran, cancellationToken: cancellationToken), (first, second, third, fourth) => (first, second, third, fourth)); - public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param, IDbTransaction tran = null) - => conn.QueryAsync(rawSql, (first, second, third, fourth, fifth) => (first, second, third, fourth, fifth), param: param, transaction: tran); + public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(new CommandDefinition(rawSql, param, transaction: tran, cancellationToken: cancellationToken), (first, second, third, fourth, fifth) => (first, second, third, fourth, fifth)); - public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null) - => conn.QueryAsync(rawSql, (first, second, third, fourth, fifth, sixth) => (first, second, third, fourth, fifth, sixth), param: param, transaction: tran); + public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(new CommandDefinition(rawSql, param, transaction: tran, cancellationToken: cancellationToken), (first, second, third, fourth, fifth, sixth) => (first, second, third, fourth, fifth, sixth)); - public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null) - => conn.QueryAsync(rawSql, (first, second, third, fourth, fifth, sixth, seventh) => (first, second, third, fourth, fifth, sixth, seventh), param: param, transaction: tran); + public static Task> QueryAsync(this IDbConnection conn, string rawSql, object param = null, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.QueryAsync(new CommandDefinition(rawSql, param, transaction: tran, cancellationToken: cancellationToken), (first, second, third, fourth, fifth, sixth, seventh) => (first, second, third, fourth, fifth, sixth, seventh)); - public static Task ExecuteAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null) - => conn.ExecuteAsync(query.RawSql, param: query.Parameters, transaction: tran); + public static Task ExecuteAsync(this IDbConnection conn, SqlBuilder.Template query, IDbTransaction tran = null, CancellationToken cancellationToken = default) + => conn.ExecuteAsync(new CommandDefinition(query.RawSql, query.Parameters, transaction: tran, cancellationToken: cancellationToken)); public static async Task ExecuteAsync(this IDbConnection conn, Func> function) { diff --git a/src/Simpleverse.Repository.Db/DbRepository.cs b/src/Simpleverse.Repository.Db/DbRepository.cs index cb13849..ab39124 100644 --- a/src/Simpleverse.Repository.Db/DbRepository.cs +++ b/src/Simpleverse.Repository.Db/DbRepository.cs @@ -1,9 +1,10 @@ -using Dapper; +using Dapper; using Simpleverse.Repository.Db.Meta; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db @@ -19,61 +20,61 @@ public DbRepository(Func connectionFactory) _connectionFactory = connectionFactory; } - public Task> QueryAsync(SqlBuilder.Template query) - => QueryAsync(query.RawSql, query.Parameters); + public Task> QueryAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default) + => QueryAsync(query.RawSql, query.Parameters, cancellationToken); - public Task> QueryAsync(SqlBuilder.Template query) - => QueryAsync(query.RawSql, query.Parameters); + public Task> QueryAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default) + => QueryAsync(query.RawSql, query.Parameters, cancellationToken); - public Task> QueryAsync(SqlBuilder.Template query) - => QueryAsync(query.RawSql, query.Parameters); + public Task> QueryAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default) + => QueryAsync(query.RawSql, query.Parameters, cancellationToken: cancellationToken); - public Task> QueryAsync(SqlBuilder.Template query) - => QueryAsync(query.RawSql, query.Parameters); + public Task> QueryAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default) + => QueryAsync(query.RawSql, query.Parameters, cancellationToken: cancellationToken); - public Task> QueryAsync(SqlBuilder.Template query) - => QueryAsync(query.RawSql, query.Parameters); + public Task> QueryAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default) + => QueryAsync(query.RawSql, query.Parameters, cancellationToken: cancellationToken); - public Task> QueryAsync(SqlBuilder.Template query) - => QueryAsync(query.RawSql, query.Parameters); + public Task> QueryAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default) + => QueryAsync(query.RawSql, query.Parameters, cancellationToken: cancellationToken); - public Task> QueryAsync(SqlBuilder.Template query) - => QueryAsync(query.RawSql, query.Parameters); + public Task> QueryAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default) + => QueryAsync(query.RawSql, query.Parameters, cancellationToken: cancellationToken); - public Task> QueryAsync(SqlBuilder.Template query) - => QueryAsync(query.RawSql, query.Parameters); + public Task> QueryAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default) + => QueryAsync(query.RawSql, query.Parameters, cancellationToken: cancellationToken); - public virtual async Task> QueryAsync(string rawSql, object parameters) + public virtual async Task> QueryAsync(string rawSql, object parameters, CancellationToken cancellationToken = default) { - return await ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters)); + return await ExecuteAsync((conn) => conn.QueryAsync(new CommandDefinition(rawSql, parameters, cancellationToken: cancellationToken))); } - public virtual async Task> QueryAsync(string rawSql, object parameters) + public virtual async Task> QueryAsync(string rawSql, object parameters, CancellationToken cancellationToken = default) { - return await ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters)); + return await ExecuteAsync((conn) => conn.QueryAsync(new CommandDefinition(rawSql, parameters, cancellationToken: cancellationToken))); } - public Task> QueryAsync(string rawSql, object parameters = null) - => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters)); + public Task> QueryAsync(string rawSql, object parameters = null, CancellationToken cancellationToken = default) + => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters, cancellationToken: cancellationToken)); - public Task> QueryAsync(string rawSql, object parameters = null) - => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters)); + public Task> QueryAsync(string rawSql, object parameters = null, CancellationToken cancellationToken = default) + => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters, cancellationToken: cancellationToken)); - public Task> QueryAsync(string rawSql, object parameters = null) - => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters)); + public Task> QueryAsync(string rawSql, object parameters = null, CancellationToken cancellationToken = default) + => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters, cancellationToken: cancellationToken)); - public Task> QueryAsync(string rawSql, object parameters = null) - => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters)); + public Task> QueryAsync(string rawSql, object parameters = null, CancellationToken cancellationToken = default) + => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters, cancellationToken: cancellationToken)); - public Task> QueryAsync(string rawSql, object parameters = null) - => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters)); + public Task> QueryAsync(string rawSql, object parameters = null, CancellationToken cancellationToken = default) + => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters, cancellationToken: cancellationToken)); - public Task> QueryAsync(string rawSql, object parameters = null) - => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters)); + public Task> QueryAsync(string rawSql, object parameters = null, CancellationToken cancellationToken = default) + => ExecuteAsync((conn) => conn.QueryAsync(rawSql, param: parameters, cancellationToken: cancellationToken)); - public Task ExecuteAsync(SqlBuilder.Template query) - => ExecuteAsync((conn) => conn.ExecuteAsync(query)); + public Task ExecuteAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default) + => ExecuteAsync((conn) => conn.ExecuteAsync(query, cancellationToken: cancellationToken)); public async Task ExecuteAsync(Func> function) { diff --git a/src/Simpleverse.Repository.Db/Entity/Entity.cs b/src/Simpleverse.Repository.Db/Entity/Entity.cs index c95d9cd..3508c54 100644 --- a/src/Simpleverse.Repository.Db/Entity/Entity.cs +++ b/src/Simpleverse.Repository.Db/Entity/Entity.cs @@ -1,4 +1,4 @@ -using Dapper; +using Dapper; using Dapper.Contrib.Extensions; using Simpleverse.Repository.ChangeTracking; using Simpleverse.Repository.Db.Extensions.Dapper; @@ -11,6 +11,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Entity @@ -36,77 +37,79 @@ public Entity(DbRepository repository, Table source) #region Get public async Task GetAsync(dynamic id) - { - return await Repository.ExecuteAsync((conn) => GetAsync(conn, id)); - } + => await Repository.ExecuteAsync((conn) => GetAsync(conn, id)); + public virtual Task GetAsync(IDbConnection connection, dynamic id, IDbTransaction transaction = null) => SqlMapperExtensions.GetAsync(connection, id, transaction: transaction); - public Task GetAsync(Action filterSetup = null, Action optionsSetup = null) - => GetAsync(filterSetup, optionsSetup); + public Task GetAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => GetAsync(filterSetup, optionsSetup, cancellationToken); public Task GetAsync( IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) - => GetAsync(connection, filterSetup, optionsSetup, transaction: transaction); + => GetAsync(connection, filterSetup, optionsSetup, transaction: transaction, cancellationToken: cancellationToken); - public async Task GetAsync(Action filterSetup = null, Action optionsSetup = null) - => (await ListAsync(filterSetup, options => { options.Take = 1; optionsSetup?.Invoke(options); })).FirstOrDefault(); - public async Task GetAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null) - => (await ListAsync(connection, filterSetup, options => { options.Take = 1; optionsSetup?.Invoke(options); }, transaction: transaction)).FirstOrDefault(); + public async Task GetAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => (await ListAsync(filterSetup, options => { options.Take = 1; optionsSetup?.Invoke(options); }, cancellationToken)).FirstOrDefault(); + public async Task GetAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => (await ListAsync(connection, filterSetup, options => { options.Take = 1; optionsSetup?.Invoke(options); }, transaction: transaction, cancellationToken: cancellationToken)).FirstOrDefault(); #endregion #region Exists - public async Task ExistsAsync(Action filterSetup = null) - => await GetAsync(filterSetup: filterSetup) != null; - public async Task ExistsAsync(IDbConnection connection, Action filterSetup = null, IDbTransaction transaction = null) - => await GetAsync(connection, filterSetup: filterSetup, transaction: transaction) != null; + public async Task ExistsAsync(Action filterSetup = null, CancellationToken cancellationToken = default) + => await GetAsync(filterSetup: filterSetup, cancellationToken: cancellationToken) != null; + public async Task ExistsAsync(IDbConnection connection, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => await GetAsync(connection, filterSetup: filterSetup, transaction: transaction, cancellationToken: cancellationToken) != null; #endregion #region List - public Task> ListAsync(Action filterSetup = null, Action optionsSetup = null) - => ListAsync(filterSetup, optionsSetup); + public Task> ListAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => ListAsync(filterSetup, optionsSetup, cancellationToken); public Task> ListAsync( IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) - => ListAsync(connection, filterSetup, optionsSetup, transaction: transaction); + => ListAsync(connection, filterSetup, optionsSetup, transaction: transaction, cancellationToken: cancellationToken); - public Task> ListAsync(Action filterSetup = null, Action optionsSetup = null) + public Task> ListAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) { var filter = GetFilter(filterSetup); var options = optionsSetup.Get(); - return ListAsync(filter, options); + return ListAsync(filter, options, cancellationToken); } public Task> ListAsync( IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) { var filter = GetFilter(filterSetup); var options = optionsSetup.Get(); - return ListAsync(connection, filter, options, transaction: transaction); + return ListAsync(connection, filter, options, transaction: transaction, cancellationToken: cancellationToken); } - public Task> ListAsync(TFilter filter, TOptions options) - => ListAsync(filter, options); - public Task> ListAsync(IDbConnection connection, TFilter filter, TOptions options, IDbTransaction transaction = null) - => ListAsync(connection, filter, options, transaction); + public Task> ListAsync(TFilter filter, TOptions options, CancellationToken cancellationToken = default) + => ListAsync(filter, options, cancellationToken); + public Task> ListAsync(IDbConnection connection, TFilter filter, TOptions options, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => ListAsync(connection, filter, options, transaction, cancellationToken); - public Task> ListAsync(TFilter filter, TOptions options) - => Repository.ExecuteAsync((conn) => ListAsync(conn, filter, options)); + public Task> ListAsync(TFilter filter, TOptions options, CancellationToken cancellationToken = default) + => Repository.ExecuteAsync((conn) => ListAsync(conn, filter, options, cancellationToken: cancellationToken)); - public virtual Task> ListAsync(IDbConnection connection, TFilter filter, TOptions options, IDbTransaction transaction = null) + public virtual Task> ListAsync(IDbConnection connection, TFilter filter, TOptions options, IDbTransaction transaction = null, CancellationToken cancellationToken = default) { var builder = Source.AsQuery(); @@ -123,12 +126,12 @@ public virtual Task> ListAsync(IDbConnection connection, TFilt return (Task>) typeof(DbConnectionExtensions) - .GetMethod(nameof(DbConnectionExtensions.QueryAsync), typeArgumentsCount, new[] { typeof(IDbConnection), query.GetType(), typeof(IDbTransaction) }) + .GetMethod(nameof(DbConnectionExtensions.QueryAsync), typeArgumentsCount, new[] { typeof(IDbConnection), query.GetType(), typeof(IDbTransaction), typeof(CancellationToken) }) .MakeGenericMethod(type.GenericTypeArguments) - .Invoke(null, new object[] { connection, query, transaction }); + .Invoke(null, new object[] { connection, query, transaction, cancellationToken }); } - return connection.QueryAsync(query, tran: transaction); + return connection.QueryAsync(query, tran: transaction, cancellationToken: cancellationToken); } protected virtual void SelectQuery(QueryBuilder builder, TFilter filter, TOptions options) @@ -154,19 +157,20 @@ protected void Query(QueryBuilder builder, TFilter filter) #region Add - public Task AddAsync(TModel model) - => AddAsync(new[] { model }); + public Task AddAsync(TModel model, CancellationToken cancellationToken = default) + => AddAsync(new[] { model }, cancellationToken); - public Task AddAsync(IEnumerable models) - => AddAsync(models, outputMap: OutputMapper.MapOnce); + public Task AddAsync(IEnumerable models, CancellationToken cancellationToken = default) + => AddAsync(models, outputMap: OutputMapper.MapOnce, cancellationToken: cancellationToken); public async Task AddAsync( IEnumerable models, - Action, IEnumerable, IEnumerable, IEnumerable> outputMap + Action, IEnumerable, IEnumerable, IEnumerable> outputMap, + CancellationToken cancellationToken = default ) { return await Repository.ExecuteAsync( - (conn) => AddAsync(conn, models, outputMap: outputMap) + (conn) => AddAsync(conn, models, outputMap: outputMap, cancellationToken: cancellationToken) ); } @@ -174,7 +178,8 @@ public virtual Task AddAsync( IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) { if (Repository is SqlRepository) @@ -182,7 +187,8 @@ public virtual Task AddAsync( return connection.InsertBulkAsync( models, transaction: transaction, - outputMap: outputMap + outputMap: outputMap, + cancellationToken: cancellationToken ); } @@ -198,19 +204,20 @@ public virtual Task AddAsync( #region ByModel - public Task UpdateAsync(TModel model) - => UpdateAsync(new[] { model }); + public Task UpdateAsync(TModel model, CancellationToken cancellationToken = default) + => UpdateAsync(new[] { model }, cancellationToken); - public Task UpdateAsync(IEnumerable models) - => UpdateAsync(models, outputMap: OutputMapper.MapOnce); + public Task UpdateAsync(IEnumerable models, CancellationToken cancellationToken = default) + => UpdateAsync(models, outputMap: OutputMapper.MapOnce, cancellationToken: cancellationToken); public async Task UpdateAsync( IEnumerable models, - Action, IEnumerable, IEnumerable, IEnumerable> outputMap + Action, IEnumerable, IEnumerable, IEnumerable> outputMap, + CancellationToken cancellationToken = default ) { return await Repository.ExecuteAsync( - (conn) => UpdateAsync(conn, models, outputMap: outputMap) + (conn) => UpdateAsync(conn, models, outputMap: outputMap, cancellationToken: cancellationToken) ); } @@ -218,7 +225,8 @@ public virtual async Task UpdateAsync( IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) { if (Repository is SqlRepository) @@ -226,7 +234,8 @@ public virtual async Task UpdateAsync( return await connection.UpdateBulkAsync( models, transaction: transaction, - outputMap: outputMap + outputMap: outputMap, + cancellationToken: cancellationToken ); } @@ -243,12 +252,12 @@ public virtual async Task UpdateAsync( #endregion - public virtual Task UpdateAsync(Action updateSetup, Action filterSetup = null, Action optionsSetup = null) + public virtual Task UpdateAsync(Action updateSetup, Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) => Repository.ExecuteAsync( - (conn) => UpdateAsync(conn, updateSetup, filterSetup, optionsSetup) + (conn) => UpdateAsync(conn, updateSetup, filterSetup, optionsSetup, cancellationToken: cancellationToken) ); - public virtual Task UpdateAsync(IDbConnection connection, Action updateSetup, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null) + public virtual Task UpdateAsync(IDbConnection connection, Action updateSetup, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) { var update = GetUpdate(updateSetup); var filter = GetFilter(filterSetup); @@ -258,7 +267,7 @@ public virtual Task UpdateAsync(IDbConnection connection, Action u UpdateQuery(builder, update, filter, options); var query = UpdateTemplate(builder, update, filter, options); - return connection.ExecuteAsync(query, tran: transaction); + return connection.ExecuteAsync(query, tran: transaction, cancellationToken: cancellationToken); } protected virtual void UpdateQuery(QueryBuilder builder, TUpdate update, TFilter filter, TOptions options) @@ -327,19 +336,20 @@ protected virtual void Set(QueryBuilder builder, TUpdate update) #region Upsert - public Task UpsertAsync(TModel model) - => UpsertAsync(new[] { model }); + public Task UpsertAsync(TModel model, CancellationToken cancellationToken = default) + => UpsertAsync(new[] { model }, cancellationToken); - public Task UpsertAsync(IEnumerable models) - => UpsertAsync(models, outputMap: OutputMapper.MapOnce); + public Task UpsertAsync(IEnumerable models, CancellationToken cancellationToken = default) + => UpsertAsync(models, outputMap: OutputMapper.MapOnce, cancellationToken: cancellationToken); public async Task UpsertAsync( IEnumerable models, - Action, IEnumerable, IEnumerable, IEnumerable> outputMap + Action, IEnumerable, IEnumerable, IEnumerable> outputMap, + CancellationToken cancellationToken = default ) { return await Repository.ExecuteAsync( - (conn) => UpsertAsync(conn, models, outputMap: outputMap) + (conn) => UpsertAsync(conn, models, outputMap: outputMap, cancellationToken: cancellationToken) ); } @@ -347,13 +357,14 @@ public virtual Task UpsertAsync( IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) { if (!(Repository is SqlRepository)) throw new NotSupportedException("Upsert is not supported on non-SQL repository connections."); - return connection.UpsertBulkAsync(models, transaction: transaction, outputMap: outputMap); + return connection.UpsertBulkAsync(models, transaction: transaction, outputMap: outputMap, cancellationToken: cancellationToken); } #endregion @@ -362,26 +373,27 @@ public virtual Task UpsertAsync( #region ByModel - public async Task DeleteAsync(TModel model) + public async Task DeleteAsync(TModel model, CancellationToken cancellationToken = default) { return await Repository.ExecuteAsync((conn) => DeleteAsync(conn, model)); } - public virtual Task DeleteAsync(IDbConnection connection, TModel model, IDbTransaction transaction = null) + public virtual Task DeleteAsync(IDbConnection connection, TModel model, IDbTransaction transaction = null, CancellationToken cancellationToken = default) => connection.DeleteAsync(model, transaction: transaction); - public async Task DeleteAsync(IEnumerable models) + public async Task DeleteAsync(IEnumerable models, CancellationToken cancellationToken = default) { return await Repository.ExecuteAsync( - (conn) => DeleteAsync(conn, models) + (conn) => DeleteAsync(conn, models, cancellationToken: cancellationToken) ); } - public virtual async Task DeleteAsync(IDbConnection connection, IEnumerable models, IDbTransaction transaction = null) + public virtual async Task DeleteAsync(IDbConnection connection, IEnumerable models, IDbTransaction transaction = null, CancellationToken cancellationToken = default) { if (Repository is SqlRepository) { return await connection.DeleteBulkAsync( models, - transaction: transaction + transaction: transaction, + cancellationToken: cancellationToken ); } @@ -394,13 +406,14 @@ public virtual async Task DeleteAsync(IDbConnection connection, IEnumerable #endregion - public Task DeleteAsync(Action filterSetup = null, Action optionsSetup = null) - => Repository.ExecuteAsync((conn) => DeleteAsync(conn, filterSetup, optionsSetup)); + public Task DeleteAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => Repository.ExecuteAsync((conn) => DeleteAsync(conn, filterSetup, optionsSetup, cancellationToken: cancellationToken)); public virtual Task DeleteAsync( IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) { var filter = GetFilter(filterSetup); @@ -409,7 +422,7 @@ public virtual Task DeleteAsync( DeleteQuery(builder, filter, options); var query = DeleteTemplate(builder, options); - return connection.ExecuteAsync(query, tran: transaction); + return connection.ExecuteAsync(query, tran: transaction, cancellationToken: cancellationToken); } protected virtual void DeleteQuery(QueryBuilder builder, TFilter filter, TOptions options) @@ -424,38 +437,39 @@ protected virtual SqlBuilder.Template DeleteTemplate(QueryBuilder builde #region IAggregate - #region Min + #region Min - public Task MinAsync(string columnName) + public Task MinAsync(string columnName, CancellationToken cancellationToken = default) where TResult : struct - => MinAsync(Source.Column(columnName), null); - public Task MinAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null) + => MinAsync(Source.Column(columnName), null, cancellationToken); + public Task MinAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct - => MinAsync(connection, Source.Column(columnName), null, transaction: transaction); + => MinAsync(connection, Source.Column(columnName), null, transaction: transaction, cancellationToken: cancellationToken); - public Task MinAsync(string columnName, Action filterSetup = null) + public Task MinAsync(string columnName, Action filterSetup = null, CancellationToken cancellationToken = default) where TResult : struct - => MinAsync(Source.Column(columnName), filterSetup); - public Task MinAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null) + => MinAsync(Source.Column(columnName), filterSetup, cancellationToken); + public Task MinAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct - => MinAsync(connection, Source.Column(columnName), filterSetup, transaction: transaction); + => MinAsync(connection, Source.Column(columnName), filterSetup, transaction: transaction, cancellationToken: cancellationToken); - public Task MinAsync(Expression> columnExpression, Action filterSetup = null) + public Task MinAsync(Expression> columnExpression, Action filterSetup = null, CancellationToken cancellationToken = default) where TResult : struct - => MinAsync(Source.Column(columnExpression), filterSetup); - public Task MinAsync(IDbConnection connection, Expression> columnExpression, Action filterSetup = null, IDbTransaction transaction = null) + => MinAsync(Source.Column(columnExpression), filterSetup, cancellationToken); + public Task MinAsync(IDbConnection connection, Expression> columnExpression, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct - => MinAsync(connection, Source.Column(columnExpression), filterSetup, transaction: transaction); + => MinAsync(connection, Source.Column(columnExpression), filterSetup, transaction: transaction, cancellationToken: cancellationToken); - protected virtual Task MinAsync(Selector column, Action filterSetup = null) + protected virtual Task MinAsync(Selector column, Action filterSetup = null, CancellationToken cancellationToken = default) where TResult : struct - => Repository.ExecuteAsyncWithTransaction((conn, tran) => MinAsync(conn, column, filterSetup, tran)); + => Repository.ExecuteAsyncWithTransaction((conn, tran) => MinAsync(conn, column, filterSetup, tran, cancellationToken)); public virtual Task MinAsync( IDbConnection connection, Selector column, Action filterSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) where TResult : struct { @@ -472,42 +486,43 @@ protected virtual SqlBuilder.Template DeleteTemplate(QueryBuilder builde "); Filter(builder, GetFilter(filterSetup)); - return Repository.ExecuteAsync((conn) => conn.QueryFirstOrDefaultAsync(query.RawSql, query.Parameters)); + return Repository.ExecuteAsync((conn) => conn.QueryFirstOrDefaultAsync(new CommandDefinition(query.RawSql, query.Parameters, cancellationToken: cancellationToken))); } #endregion #region Max - public Task MaxAsync(string columnName) + public Task MaxAsync(string columnName, CancellationToken cancellationToken = default) where TResult : struct - => MaxAsync(Source.Column(columnName), null); - public Task MaxAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null) + => MaxAsync(Source.Column(columnName), null, cancellationToken); + public Task MaxAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct - => MaxAsync(connection, Source.Column(columnName), null, transaction: transaction); + => MaxAsync(connection, Source.Column(columnName), null, transaction: transaction, cancellationToken: cancellationToken); - public Task MaxAsync(string columnName, Action filterSetup = null) + public Task MaxAsync(string columnName, Action filterSetup = null, CancellationToken cancellationToken = default) where TResult : struct - => MaxAsync(Source.Column(columnName), filterSetup); - public Task MaxAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null) + => MaxAsync(Source.Column(columnName), filterSetup, cancellationToken); + public Task MaxAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct - => MaxAsync(connection, Source.Column(columnName), filterSetup, transaction: transaction); + => MaxAsync(connection, Source.Column(columnName), filterSetup, transaction: transaction, cancellationToken: cancellationToken); - public Task MaxAsync(Expression> columnExpression, Action filterSetup = null) + public Task MaxAsync(Expression> columnExpression, Action filterSetup = null, CancellationToken cancellationToken = default) where TResult : struct - => MaxAsync(Source.Column(columnExpression), filterSetup); - public Task MaxAsync(IDbConnection connection, Expression> columnExpression, Action filterSetup = null, IDbTransaction transaction = null) + => MaxAsync(Source.Column(columnExpression), filterSetup, cancellationToken); + public Task MaxAsync(IDbConnection connection, Expression> columnExpression, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct - => MaxAsync(connection, Source.Column(columnExpression), filterSetup, transaction); + => MaxAsync(connection, Source.Column(columnExpression), filterSetup, transaction, cancellationToken); - protected virtual Task MaxAsync(Selector column, Action filterSetup = null) + protected virtual Task MaxAsync(Selector column, Action filterSetup = null, CancellationToken cancellationToken = default) where TResult : struct - => Repository.ExecuteAsync((conn) => MaxAsync(conn, column)); + => Repository.ExecuteAsync((conn) => MaxAsync(conn, column, cancellationToken: cancellationToken)); public virtual Task MaxAsync( IDbConnection connection, Selector column, Action filterSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) where TResult : struct { @@ -525,7 +540,7 @@ protected virtual SqlBuilder.Template DeleteTemplate(QueryBuilder builde /**where**/ " ); - return connection.QueryFirstOrDefaultAsync(query.RawSql, query.Parameters, transaction: transaction); + return connection.QueryFirstOrDefaultAsync(new CommandDefinition(query.RawSql, query.Parameters, transaction: transaction, cancellationToken: cancellationToken)); } #endregion @@ -536,15 +551,17 @@ protected virtual SqlBuilder.Template DeleteTemplate(QueryBuilder builde public Task<(int Deleted, int Added)> ReplaceAsync( Action filterSetup, - IEnumerable models + IEnumerable models, + CancellationToken cancellationToken = default ) - => Repository.ExecuteAsyncWithTransaction((conn, tran) => ReplaceAsync(conn, tran, filterSetup, models)); + => Repository.ExecuteAsyncWithTransaction((conn, tran) => ReplaceAsync(conn, tran, filterSetup, models, cancellationToken)); public virtual Task<(int Deleted, int Added)> ReplaceAsync( IDbConnection conn, IDbTransaction tran, Action filterSetup, - IEnumerable models + IEnumerable models, + CancellationToken cancellationToken = default ) { return conn.ExecuteAsyncWithTransaction( @@ -553,7 +570,8 @@ IEnumerable models var deleted = await DeleteAsync( conn, filterSetup, - transaction: tran + transaction: tran, + cancellationToken: cancellationToken ); if (models == null) @@ -563,7 +581,8 @@ IEnumerable models conn, models, outputMap: OutputMapper.Map, - transaction: tran + transaction: tran, + cancellationToken: cancellationToken ); return (deleted, added); diff --git a/src/Simpleverse.Repository.Db/Entity/ProjectedEntity.cs b/src/Simpleverse.Repository.Db/Entity/ProjectedEntity.cs index dd002ea..41ef9cf 100644 --- a/src/Simpleverse.Repository.Db/Entity/ProjectedEntity.cs +++ b/src/Simpleverse.Repository.Db/Entity/ProjectedEntity.cs @@ -1,9 +1,10 @@ -using Simpleverse.Repository.Entity; +using Simpleverse.Repository.Entity; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Entity @@ -29,38 +30,39 @@ public ProjectedEntity(IEntity entity, Func< #region IAdd - public Task AddAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => AddAsync(conn, models, outputMap, tran)); + public Task AddAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => AddAsync(conn, models, outputMap, tran, cancellationToken)); public virtual Task AddAsync( IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ) { - return _entity.AddAsync(connection, models.Select(x => x.Model), OutputMapRedirect(models, outputMap), transaction: transaction); + return _entity.AddAsync(connection, models.Select(x => x.Model), OutputMapRedirect(models, outputMap), transaction: transaction, cancellationToken: cancellationToken); } #endregion #region IDelete - public override sealed Task DeleteAsync(Action filterSetup = null, Action optionsSetup = null) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => DeleteAsync(conn, filterSetup, optionsSetup, tran)); + public override sealed Task DeleteAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => DeleteAsync(conn, filterSetup, optionsSetup, tran, cancellationToken)); - public virtual Task DeleteAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null) - => _entity.DeleteAsync(connection, filterSetup, optionsSetup, transaction: transaction); + public virtual Task DeleteAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => _entity.DeleteAsync(connection, filterSetup, optionsSetup, transaction: transaction, cancellationToken: cancellationToken); - public async Task DeleteAsync(IDbConnection connection, TProjection model, IDbTransaction transaction = null) - => await DeleteAsync(connection, new[] { model }, transaction) > 0; + public async Task DeleteAsync(IDbConnection connection, TProjection model, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => await DeleteAsync(connection, new[] { model }, transaction, cancellationToken) > 0; - public override sealed Task DeleteAsync(IEnumerable models) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => DeleteAsync(conn, models, tran)); + public override sealed Task DeleteAsync(IEnumerable models, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => DeleteAsync(conn, models, tran, cancellationToken)); - public virtual Task DeleteAsync(IDbConnection connection, IEnumerable models, IDbTransaction transaction = null) + public virtual Task DeleteAsync(IDbConnection connection, IEnumerable models, IDbTransaction transaction = null, CancellationToken cancellationToken = default) { - return _entity.DeleteAsync(connection, models.Select(x => x.Model), transaction: transaction); + return _entity.DeleteAsync(connection, models.Select(x => x.Model), transaction: transaction, cancellationToken: cancellationToken); } #endregion @@ -69,59 +71,59 @@ public virtual Task DeleteAsync(IDbConnection connection, IEnumerable ExistsAsync(Action filterSetup = null) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => ExistsAsync(conn, filterSetup, tran)); + public override sealed Task ExistsAsync(Action filterSetup = null, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => ExistsAsync(conn, filterSetup, tran, cancellationToken)); - public virtual Task ExistsAsync(IDbConnection connection, Action filterSetup = null, IDbTransaction transaction = null) - => _entity.ExistsAsync(connection, filterSetup, transaction: transaction); + public virtual Task ExistsAsync(IDbConnection connection, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => _entity.ExistsAsync(connection, filterSetup, transaction: transaction, cancellationToken: cancellationToken); #endregion #region Get - public async Task GetAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null) + public async Task GetAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) { - var model = await GetAsync(connection, filterSetup, optionsSetup, transaction: transaction); + var model = await GetAsync(connection, filterSetup, optionsSetup, transaction: transaction, cancellationToken: cancellationToken); if (model == null) return default; return Instance(model); } - public override sealed async Task GetAsync(Action filterSetup = null, Action optionsSetup = null) - => (await ListAsync(filterSetup, options => { options.Take = 1; optionsSetup?.Invoke(options); })).FirstOrDefault(); + public override sealed async Task GetAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => (await ListAsync(filterSetup, options => { options.Take = 1; optionsSetup?.Invoke(options); }, cancellationToken)).FirstOrDefault(); - public async Task GetAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null) - => (await ListAsync(connection, filterSetup, options => { options.Take = 1; optionsSetup?.Invoke(options); }, transaction: transaction)).FirstOrDefault(); + public async Task GetAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => (await ListAsync(connection, filterSetup, options => { options.Take = 1; optionsSetup?.Invoke(options); }, transaction: transaction, cancellationToken: cancellationToken)).FirstOrDefault(); #endregion #region List - public Task> ListAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null) - => ListAsync(connection, GetFilter(filterSetup), optionsSetup.Get(), transaction: transaction); + public Task> ListAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => ListAsync(connection, GetFilter(filterSetup), optionsSetup.Get(), transaction: transaction, cancellationToken: cancellationToken); - public Task> ListAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null) - => ListAsync(connection, GetFilter(filterSetup), optionsSetup.Get(), transaction: transaction); + public Task> ListAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => ListAsync(connection, GetFilter(filterSetup), optionsSetup.Get(), transaction: transaction, cancellationToken: cancellationToken); - public override sealed Task> ListAsync(TFilter filter, TOptions options) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => ListAsync(conn, filter, options, tran)); + public override sealed Task> ListAsync(TFilter filter, TOptions options, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => ListAsync(conn, filter, options, tran, cancellationToken)); - public virtual async Task> ListAsync(IDbConnection connection, TFilter filter, TOptions options, IDbTransaction transaction = null) + public virtual async Task> ListAsync(IDbConnection connection, TFilter filter, TOptions options, IDbTransaction transaction = null, CancellationToken cancellationToken = default) { - var models = await _entity.ListAsync(connection, filter, options, transaction); + var models = await _entity.ListAsync(connection, filter, options, transaction, cancellationToken); if (models == null) return default; return models.Select(Instance); } - public override sealed Task> ListAsync(TFilter filter, TOptions options) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => ListAsync(conn, filter, options, tran)); + public override sealed Task> ListAsync(TFilter filter, TOptions options, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => ListAsync(conn, filter, options, tran, cancellationToken)); - public virtual Task> ListAsync(IDbConnection connection, TFilter filter, TOptions options, IDbTransaction transaction = null) + public virtual Task> ListAsync(IDbConnection connection, TFilter filter, TOptions options, IDbTransaction transaction = null, CancellationToken cancellationToken = default) { - return _entity.ListAsync(connection, filter, options, transaction: transaction); + return _entity.ListAsync(connection, filter, options, transaction: transaction, cancellationToken: cancellationToken); } #endregion @@ -132,27 +134,27 @@ public virtual Task> ListAsync(IDbConnection connection, TFilt #region Max - public override sealed Task MaxAsync(string columName, Action filterSetup) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => MaxAsync(conn, columName, filterSetup, tran)); + public override sealed Task MaxAsync(string columName, Action filterSetup, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => MaxAsync(conn, columName, filterSetup, tran, cancellationToken)); - public Task MaxAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null) where TResult : struct - => MaxAsync(connection, columnName, null, transaction: transaction); + public Task MaxAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct + => MaxAsync(connection, columnName, null, transaction: transaction, cancellationToken: cancellationToken); - public virtual Task MaxAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null) where TResult : struct - => _entity.MaxAsync(connection, columnName, filterSetup, transaction: transaction); + public virtual Task MaxAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct + => _entity.MaxAsync(connection, columnName, filterSetup, transaction: transaction, cancellationToken: cancellationToken); #endregion #region Min - public override sealed Task MinAsync(string columName, Action filterSetup) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => MinAsync(conn, columName, filterSetup, tran)); + public override sealed Task MinAsync(string columName, Action filterSetup, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => MinAsync(conn, columName, filterSetup, tran, cancellationToken)); - public Task MinAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null) where TResult : struct - => MinAsync(connection, columnName, null, transaction: transaction); + public Task MinAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct + => MinAsync(connection, columnName, null, transaction: transaction, cancellationToken: cancellationToken); - public Task MinAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null) where TResult : struct - => _entity.MinAsync(connection, columnName, filterSetup, transaction: transaction); + public Task MinAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct + => _entity.MinAsync(connection, columnName, filterSetup, transaction: transaction, cancellationToken: cancellationToken); #endregion @@ -160,37 +162,37 @@ public virtual Task> ListAsync(IDbConnection connection, TFilt #region IReplace - public override sealed Task<(int Deleted, int Added)> ReplaceAsync(Action filterSetup, IEnumerable models) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => ReplaceAsync(conn, tran, filterSetup, models)); + public override sealed Task<(int Deleted, int Added)> ReplaceAsync(Action filterSetup, IEnumerable models, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => ReplaceAsync(conn, tran, filterSetup, models, cancellationToken)); - public virtual Task<(int Deleted, int Added)> ReplaceAsync(IDbConnection conn, IDbTransaction tran, Action filterSetup, IEnumerable models) - => _entity.ReplaceAsync(conn, tran, filterSetup, models.Select(x => x.Model)); + public virtual Task<(int Deleted, int Added)> ReplaceAsync(IDbConnection conn, IDbTransaction tran, Action filterSetup, IEnumerable models, CancellationToken cancellationToken = default) + => _entity.ReplaceAsync(conn, tran, filterSetup, models.Select(x => x.Model), cancellationToken); #endregion #region IUpdate - public override sealed Task UpdateAsync(Action updateSetup, Action filterSetup = null, Action optionsSetup = null) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => UpdateAsync(conn, updateSetup, filterSetup, optionsSetup, tran)); + public override sealed Task UpdateAsync(Action updateSetup, Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => UpdateAsync(conn, updateSetup, filterSetup, optionsSetup, tran, cancellationToken)); - public Task UpdateAsync(IDbConnection connection, Action updateSetup, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null) - => _entity.UpdateAsync(connection, updateSetup, filterSetup, optionsSetup, transaction: transaction); + public Task UpdateAsync(IDbConnection connection, Action updateSetup, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => _entity.UpdateAsync(connection, updateSetup, filterSetup, optionsSetup, transaction: transaction, cancellationToken: cancellationToken); - public Task UpdateAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => UpdateAsync(conn, models, outputMap, tran)); + public Task UpdateAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => UpdateAsync(conn, models, outputMap, tran, cancellationToken)); - public Task UpdateAsync(IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, IDbTransaction transaction = null) - => _entity.UpdateAsync(connection, models.Select(x => x.Model), OutputMapRedirect(models, outputMap), transaction); + public Task UpdateAsync(IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => _entity.UpdateAsync(connection, models.Select(x => x.Model), OutputMapRedirect(models, outputMap), transaction, cancellationToken); #endregion #region IUpsert - public Task UpsertAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap) - => _entity.ExecuteAsyncWithTransaction((conn, tran) => UpsertAsync(conn, models, outputMap, tran)); + public Task UpsertAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap, CancellationToken cancellationToken = default) + => _entity.ExecuteAsyncWithTransaction((conn, tran) => UpsertAsync(conn, models, outputMap, tran, cancellationToken)); - public Task UpsertAsync(IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, IDbTransaction transaction = null) - => _entity.UpsertAsync(connection, models.Select(x => x.Model), outputMap: OutputMapRedirect(models, outputMap), transaction: transaction); + public Task UpsertAsync(IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + => _entity.UpsertAsync(connection, models.Select(x => x.Model), outputMap: OutputMapRedirect(models, outputMap), transaction: transaction, cancellationToken: cancellationToken); #endregion diff --git a/src/Simpleverse.Repository.Db/Extensions/TruncateExtensions.cs b/src/Simpleverse.Repository.Db/Extensions/TruncateExtensions.cs index c0f8568..95f9bba 100644 --- a/src/Simpleverse.Repository.Db/Extensions/TruncateExtensions.cs +++ b/src/Simpleverse.Repository.Db/Extensions/TruncateExtensions.cs @@ -1,4 +1,5 @@ using System.Data; +using System.Threading; using System.Threading.Tasks; using Dapper; using Simpleverse.Repository.Db.Meta; @@ -15,22 +16,24 @@ this IDbConnection connection } public static async Task TruncateAsync( - this IDbConnection connection + this IDbConnection connection, + CancellationToken cancellationToken = default ) { var typeMeta = TypeMeta.Get(); - return await connection.TruncateAsync(typeMeta.TableName); + return await connection.TruncateAsync(typeMeta.TableName, cancellationToken); } public static async Task TruncateAsync( this IDbConnection connection, - string tableName + string tableName, + CancellationToken cancellationToken = default ) { var wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) connection.Open(); - var result = await connection.ExecuteAsync($"TRUNCATE TABLE {tableName};"); + var result = await connection.ExecuteAsync(new CommandDefinition($"TRUNCATE TABLE {tableName};", cancellationToken: cancellationToken)); if (wasClosed) connection.Close(); diff --git a/src/Simpleverse.Repository.Db/IDbRepository.cs b/src/Simpleverse.Repository.Db/IDbRepository.cs index 4b9f327..79ea413 100644 --- a/src/Simpleverse.Repository.Db/IDbRepository.cs +++ b/src/Simpleverse.Repository.Db/IDbRepository.cs @@ -1,13 +1,14 @@ using Dapper; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db { public interface IDbRepository { - Task ExecuteAsync(SqlBuilder.Template query); - Task> QueryAsync(string rawSql, object parameters); - Task> QueryAsync(string rawSql, object parameters); + Task ExecuteAsync(SqlBuilder.Template query, CancellationToken cancellationToken = default); + Task> QueryAsync(string rawSql, object parameters, CancellationToken cancellationToken = default); + Task> QueryAsync(string rawSql, object parameters, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/Simpleverse.Repository.Db/Operations/IAddDb.cs b/src/Simpleverse.Repository.Db/Operations/IAddDb.cs index 76b39b4..907d3c3 100644 --- a/src/Simpleverse.Repository.Db/Operations/IAddDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IAddDb.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Operations @@ -10,12 +11,13 @@ namespace Simpleverse.Repository.Db.Operations public interface IAddDb : IAdd where T : class { - Task AddAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap); + Task AddAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap, CancellationToken cancellationToken = default); Task AddAsync( IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ); } } diff --git a/src/Simpleverse.Repository.Db/Operations/IAggregateDb.cs b/src/Simpleverse.Repository.Db/Operations/IAggregateDb.cs index 1a72aa6..0da9087 100644 --- a/src/Simpleverse.Repository.Db/Operations/IAggregateDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IAggregateDb.cs @@ -1,25 +1,26 @@ using Simpleverse.Repository.Operations; using System; using System.Data; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Operations { public interface IAggregateDb : IAggregate { - Task MaxAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null) + Task MaxAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct; - Task MinAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null) + Task MinAsync(IDbConnection connection, string columnName, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct; } public interface IAggregateDb : IAggregateDb, IAggregate { - Task MaxAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null) + Task MaxAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct; - Task MinAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null) + Task MinAsync(IDbConnection connection, string columnName, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) where TResult : struct; } } diff --git a/src/Simpleverse.Repository.Db/Operations/IDeleteDb.cs b/src/Simpleverse.Repository.Db/Operations/IDeleteDb.cs index 77a0de6..07403d2 100644 --- a/src/Simpleverse.Repository.Db/Operations/IDeleteDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IDeleteDb.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Operations @@ -9,8 +10,8 @@ namespace Simpleverse.Repository.Db.Operations public interface IDeleteDb : IDelete where T : class { - Task DeleteAsync(IDbConnection connection, T model, IDbTransaction transaction = null); - Task DeleteAsync(IDbConnection connection, IEnumerable models, IDbTransaction transaction = null); + Task DeleteAsync(IDbConnection connection, T model, IDbTransaction transaction = null, CancellationToken cancellationToken = default); + Task DeleteAsync(IDbConnection connection, IEnumerable models, IDbTransaction transaction = null, CancellationToken cancellationToken = default); } public interface IDeleteDb : IDeleteDb, IDelete @@ -18,6 +19,6 @@ public interface IDeleteDb : IDeleteDb, IDele where TFilter : class where TOptions : class { - Task DeleteAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null); + Task DeleteAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/Simpleverse.Repository.Db/Operations/IQueryExistDb.cs b/src/Simpleverse.Repository.Db/Operations/IQueryExistDb.cs index de778a7..78612b6 100644 --- a/src/Simpleverse.Repository.Db/Operations/IQueryExistDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IQueryExistDb.cs @@ -1,6 +1,7 @@ using Simpleverse.Repository.Operations; using System; using System.Data; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Operations @@ -8,6 +9,6 @@ namespace Simpleverse.Repository.Db.Operations public interface IQueryExistDb : IQueryExist where TFilter : class { - Task ExistsAsync(IDbConnection connection, Action filterSetup = null, IDbTransaction transaction = null); + Task ExistsAsync(IDbConnection connection, Action filterSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default); } } diff --git a/src/Simpleverse.Repository.Db/Operations/IQueryGetDb.cs b/src/Simpleverse.Repository.Db/Operations/IQueryGetDb.cs index 6f5fdb8..c3adcc9 100644 --- a/src/Simpleverse.Repository.Db/Operations/IQueryGetDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IQueryGetDb.cs @@ -1,6 +1,7 @@ using Simpleverse.Repository.Operations; using System; using System.Data; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Operations @@ -14,14 +15,16 @@ Task GetAsync( IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ); Task GetAsync( IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ); } } diff --git a/src/Simpleverse.Repository.Db/Operations/IQueryListDb.cs b/src/Simpleverse.Repository.Db/Operations/IQueryListDb.cs index 87c36ea..d9533db 100644 --- a/src/Simpleverse.Repository.Db/Operations/IQueryListDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IQueryListDb.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Operations @@ -15,28 +16,32 @@ Task> ListAsync( IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ); Task> ListAsync( IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ); Task> ListAsync( IDbConnection connection, TFilter filter, TOptions options, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ); Task> ListAsync( IDbConnection connection, TFilter filter, TOptions options, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ); } } diff --git a/src/Simpleverse.Repository.Db/Operations/IReplaceDb.cs b/src/Simpleverse.Repository.Db/Operations/IReplaceDb.cs index 07b8d56..0eeb9d1 100644 --- a/src/Simpleverse.Repository.Db/Operations/IReplaceDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IReplaceDb.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Operations @@ -14,7 +15,8 @@ public interface IReplaceDb : IReplace IDbConnection conn, IDbTransaction tran, Action filterSetup, - IEnumerable models + IEnumerable models, + CancellationToken cancellationToken = default ); } } diff --git a/src/Simpleverse.Repository.Db/Operations/IUpdateDb.cs b/src/Simpleverse.Repository.Db/Operations/IUpdateDb.cs index ddc0788..8a64974 100644 --- a/src/Simpleverse.Repository.Db/Operations/IUpdateDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IUpdateDb.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Operations @@ -10,12 +11,13 @@ namespace Simpleverse.Repository.Db.Operations public interface IUpdateDb : IUpdate where T : class { - Task UpdateAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap); + Task UpdateAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap, CancellationToken cancellationToken = default); Task UpdateAsync( IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, - IDbTransaction transaction = null + IDbTransaction transaction = null, + CancellationToken cancellationToken = default ); } @@ -24,6 +26,6 @@ public interface IUpdateDb : IUpdate UpdateAsync(IDbConnection connection, Action updateSetup, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null); + Task UpdateAsync(IDbConnection connection, Action updateSetup, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default); } } diff --git a/src/Simpleverse.Repository.Db/Operations/IUpsertDb.cs b/src/Simpleverse.Repository.Db/Operations/IUpsertDb.cs index 39774c4..b607689 100644 --- a/src/Simpleverse.Repository.Db/Operations/IUpsertDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IUpsertDb.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.Operations @@ -10,7 +11,7 @@ namespace Simpleverse.Repository.Db.Operations public interface IUpsertDb : IUpsert where T : class { - Task UpsertAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap); - Task UpsertAsync(IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, IDbTransaction transaction = null); + Task UpsertAsync(IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap, CancellationToken cancellationToken = default); + Task UpsertAsync(IDbConnection connection, IEnumerable models, Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default); } } diff --git a/src/Simpleverse.Repository.Db/Simpleverse.Repository.Db.csproj b/src/Simpleverse.Repository.Db/Simpleverse.Repository.Db.csproj index 82f2543..1f6a255 100644 --- a/src/Simpleverse.Repository.Db/Simpleverse.Repository.Db.csproj +++ b/src/Simpleverse.Repository.Db/Simpleverse.Repository.Db.csproj @@ -30,7 +30,7 @@ - + True \ @@ -43,13 +43,13 @@ - - - - - - - + + + + + + + diff --git a/src/Simpleverse.Repository.Db/SqlServer/BulkExtensions.cs b/src/Simpleverse.Repository.Db/SqlServer/BulkExtensions.cs index f5e6eab..8782649 100644 --- a/src/Simpleverse.Repository.Db/SqlServer/BulkExtensions.cs +++ b/src/Simpleverse.Repository.Db/SqlServer/BulkExtensions.cs @@ -10,6 +10,7 @@ using System.Data; using System.Linq; using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.SqlServer @@ -20,7 +21,8 @@ public static async Task TransferBulkAsync( this IDbConnection connection, IEnumerable entitiesToInsert, SqlTransaction transaction = null, - Action sqlBulkCopy = null + Action sqlBulkCopy = null, + CancellationToken cancellationToken = default ) { var meta = TypeMeta.Get(); @@ -51,7 +53,8 @@ public static async Task TransferBulkAsync( string tableName, IEnumerable columnsToCopy, IDbTransaction transaction = null, - Action sqlBulkCopy = null + Action sqlBulkCopy = null, + CancellationToken cancellationToken = default ) { if (!columnsToCopy.Any()) @@ -181,7 +184,8 @@ public async static Task> GetBulkAsync( IEnumerable entitiesToGet, IDbTransaction transaction = null, int? commandTimeout = null, - Action sqlBulkCopy = null + Action sqlBulkCopy = null, + CancellationToken cancellationToken = default ) { if (!entitiesToGet.Any()) @@ -233,7 +237,8 @@ public async static Task InsertBulkAsync( IDbTransaction transaction = null, int? commandTimeout = null, Action sqlBulkCopy = null, - Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null + Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, + CancellationToken cancellationToken = default ) where T : class { var entityCount = entitiesToInsert.Count(); @@ -316,7 +321,8 @@ public async static Task UpdateBulkAsync( IDbTransaction transaction = null, int? commandTimeout = null, Action sqlBulkCopy = null, - Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null + Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, + CancellationToken cancellationToken = default ) where T : class { entitiesToUpdate = entitiesToUpdate.Where(x => x is SqlMapperExtensions.IProxy proxy && !proxy.IsDirty || !(x is SqlMapperExtensions.IProxy)); @@ -382,7 +388,8 @@ public async static Task DeleteBulkAsync( IEnumerable entitiesToDelete, IDbTransaction transaction = null, int? commandTimeout = null, - Action sqlBulkCopy = null + Action sqlBulkCopy = null, + CancellationToken cancellationToken = default ) where T : class { var entityCount = entitiesToDelete.Count(); @@ -427,7 +434,8 @@ INNER JOIN {typeMeta.TableName} AS Target IEnumerable entities, IEnumerable properties, IDbTransaction transaction = null, - Action sqlBulkCopy = null + Action sqlBulkCopy = null, + CancellationToken cancellationToken = default ) { var entityCount = entities.Count(); @@ -479,7 +487,8 @@ public static async Task ExecuteAsync( IEnumerable properties, Func, Task> executor, IDbTransaction transaction = null, - Action sqlBulkCopy = null + Action sqlBulkCopy = null, + CancellationToken cancellationToken = default ) { return await connection.ExecuteAsync( diff --git a/src/Simpleverse.Repository.Db/SqlServer/Merge/MergeExtensions.cs b/src/Simpleverse.Repository.Db/SqlServer/Merge/MergeExtensions.cs index 35f813a..a820c5d 100644 --- a/src/Simpleverse.Repository.Db/SqlServer/Merge/MergeExtensions.cs +++ b/src/Simpleverse.Repository.Db/SqlServer/Merge/MergeExtensions.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.SqlServer.Merge @@ -18,7 +19,8 @@ public async static Task UpsertAsync( IDbTransaction transaction = null, int? commandTimeout = null, Action key = null, - Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null + Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, + CancellationToken cancellationToken = default ) where T : class { @@ -27,7 +29,8 @@ public async static Task UpsertAsync( transaction: transaction, commandTimeout: commandTimeout, key: key, - outputMap: outputMap + outputMap: outputMap, + cancellationToken: cancellationToken ); } @@ -40,7 +43,8 @@ public async static Task MergeAsync( Action> matched = null, Action> notMatchedByTarget = null, Action> notMatchedBySource = null, - Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null + Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, + CancellationToken cancellationToken = default ) where T : class { @@ -52,7 +56,8 @@ public async static Task MergeAsync( matched: matched, notMatchedByTarget: notMatchedByTarget, notMatchedBySource: notMatchedBySource, - outputMap: outputMap + outputMap: outputMap, + cancellationToken: cancellationToken ); } @@ -72,7 +77,8 @@ public async static Task UpsertBulkAsync( int? commandTimeout = null, Action sqlBulkCopy = null, Action key = null, - Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null + Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, + CancellationToken cancellationToken = default ) where T : class { return await connection.MergeBulkAsync( @@ -83,7 +89,8 @@ public async static Task UpsertBulkAsync( key: key, matched: options => options.Update(), notMatchedByTarget: options => options.Insert(), - outputMap: outputMap + outputMap: outputMap, + cancellationToken: cancellationToken ); } @@ -106,7 +113,8 @@ public async static Task MergeBulkAsync( Action> matched = null, Action> notMatchedByTarget = null, Action> notMatchedBySource = null, - Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null + Action, IEnumerable, IEnumerable, IEnumerable> outputMap = null, + CancellationToken cancellationToken = default ) where T : class { if (entitiesToMerge == null) @@ -181,7 +189,8 @@ MERGE INTO {typeMeta.TableName} AS Target }, transaction: transaction, commandTimeout: commandTimeout, - outputResultsSplitConditions: new[] { "[ACTION] = 'INSERT'", "[ACTION] = 'UPDATE'" } + outputResultsSplitConditions: new[] { "[ACTION] = 'INSERT'", "[ACTION] = 'UPDATE'" }, + cancellationToken: cancellationToken ); }, transaction: transaction, diff --git a/src/Simpleverse.Repository.Db/SqlServer/OutputMapExtensions.cs b/src/Simpleverse.Repository.Db/SqlServer/OutputMapExtensions.cs index c662ca3..0985989 100644 --- a/src/Simpleverse.Repository.Db/SqlServer/OutputMapExtensions.cs +++ b/src/Simpleverse.Repository.Db/SqlServer/OutputMapExtensions.cs @@ -6,6 +6,7 @@ using System.Data; using System.Linq; using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Db.SqlServer @@ -21,7 +22,8 @@ public static async Task ExecuteWithOutputMapAsync( Action> map, IDbTransaction transaction = null, int? commandTimeout = null, - IEnumerable outputResultsSplitConditions = null + IEnumerable outputResultsSplitConditions = null, + CancellationToken cancellationToken = default ) where T : class { @@ -29,10 +31,7 @@ public static async Task ExecuteWithOutputMapAsync( async (conn, tran) => { var result = await conn.ExecuteAsync( - query, - param: parameters, - commandTimeout: commandTimeout, - transaction: tran + new CommandDefinition(query, parameters, transaction: tran, commandTimeout: commandTimeout, cancellationToken: cancellationToken) ); if (mapGeneratedValues) @@ -63,10 +62,7 @@ SELECT Target.* ); var outputs = await conn.QueryMultipleAsync( - outputSelectQuery, - param: parameters, - transaction: tran, - commandTimeout: commandTimeout + new CommandDefinition(outputSelectQuery, parameters, transaction: tran, commandTimeout: commandTimeout, cancellationToken: cancellationToken) ); outputResultsSplitConditions.ForEach( diff --git a/src/Simpleverse.Repository.Db/SqlServer/SqlConnectionExtensions.cs b/src/Simpleverse.Repository.Db/SqlServer/SqlConnectionExtensions.cs index 5af4b0a..aa634a3 100644 --- a/src/Simpleverse.Repository.Db/SqlServer/SqlConnectionExtensions.cs +++ b/src/Simpleverse.Repository.Db/SqlServer/SqlConnectionExtensions.cs @@ -17,7 +17,8 @@ public async static Task CreateTemporaryTableFromTable( string tableName, IEnumerable columns, IDbTransaction transaction, - IEnumerable arbitraryColumns = null + IEnumerable arbitraryColumns = null, + CancellationToken cancellationToken = default ) { var insertedTableName = $"#tbl_{Guid.NewGuid().ToString().Replace("-", string.Empty)}"; @@ -30,11 +31,14 @@ public async static Task CreateTemporaryTableFromTable( } await connection.ExecuteAsync( - $@"SELECT TOP 0 {columnsString} INTO {insertedTableName} FROM {tableName} WITH(NOLOCK) - UNION ALL - SELECT TOP 0 {columnsString} FROM {tableName} WITH(NOLOCK); - ", - transaction: transaction + new CommandDefinition( + $@"SELECT TOP 0 {columnsString} INTO {insertedTableName} FROM {tableName} WITH(NOLOCK) + UNION ALL + SELECT TOP 0 {columnsString} FROM {tableName} WITH(NOLOCK); + ", + transaction: transaction, + cancellationToken: cancellationToken + ) ); return insertedTableName; } diff --git a/src/Simpleverse.Repository/Entity/ProjectedEntity.cs b/src/Simpleverse.Repository/Entity/ProjectedEntity.cs index 233df1d..8f6441b 100644 --- a/src/Simpleverse.Repository/Entity/ProjectedEntity.cs +++ b/src/Simpleverse.Repository/Entity/ProjectedEntity.cs @@ -1,9 +1,10 @@ -using Simpleverse.Repository.ChangeTracking; +using Simpleverse.Repository.ChangeTracking; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Entity @@ -34,67 +35,67 @@ public ProjectedEntity(TEntity entity, Func creator) #region IAdd - public Task AddAsync(TProjection model) - => AddAsync(new[] { model }); + public Task AddAsync(TProjection model, CancellationToken cancellationToken = default) + => AddAsync(new[] { model }, cancellationToken); - public virtual Task AddAsync(IEnumerable models) - => _entity.AddAsync(models.Select(x => x.Model)); + public virtual Task AddAsync(IEnumerable models, CancellationToken cancellationToken = default) + => _entity.AddAsync(models.Select(x => x.Model), cancellationToken); #endregion #region IDelete - public virtual Task DeleteAsync(Action filterSetup = null, Action optionsSetup = null) - => _entity.DeleteAsync(filterSetup, optionsSetup); + public virtual Task DeleteAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => _entity.DeleteAsync(filterSetup, optionsSetup, cancellationToken); - public async Task DeleteAsync(TProjection model) - => await DeleteAsync(new[] { model }) > 0; + public async Task DeleteAsync(TProjection model, CancellationToken cancellationToken = default) + => await DeleteAsync(new[] { model }, cancellationToken) > 0; - public virtual Task DeleteAsync(IEnumerable models) - => _entity.DeleteAsync(models.Select(x => x.Model)); + public virtual Task DeleteAsync(IEnumerable models, CancellationToken cancellationToken = default) + => _entity.DeleteAsync(models.Select(x => x.Model), cancellationToken); #endregion #region IQuery - public virtual async Task ExistsAsync(Action filterSetup = null) - => await GetAsync(filterSetup, null) != null; + public virtual async Task ExistsAsync(Action filterSetup = null, CancellationToken cancellationToken = default) + => await GetAsync(filterSetup, null, cancellationToken) != null; #region Get - public async Task GetAsync(Action filterSetup = null, Action optionsSetup = null) + public async Task GetAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) { - var model = await GetAsync(filterSetup, optionsSetup); + var model = await GetAsync(filterSetup, optionsSetup, cancellationToken); if (model == null) return null; return Instance(model); } - public virtual async Task GetAsync(Action filterSetup = null, Action optionsSetup = null) - => (await ListAsync(filterSetup, optionsSetup)).FirstOrDefault(); + public virtual async Task GetAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => (await ListAsync(filterSetup, optionsSetup, cancellationToken)).FirstOrDefault(); #endregion #region List - public Task> ListAsync(Action filterSetup = null, Action optionsSetup = null) - => ListAsync(GetFilter(filterSetup), optionsSetup.Get()); + public Task> ListAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => ListAsync(GetFilter(filterSetup), optionsSetup.Get(), cancellationToken); - public Task> ListAsync(Action filterSetup = null, Action optionsSetup = null) - => ListAsync(GetFilter(filterSetup), optionsSetup.Get()); + public Task> ListAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => ListAsync(GetFilter(filterSetup), optionsSetup.Get(), cancellationToken); - public virtual async Task> ListAsync(TFilter filter, TOptions options) + public virtual async Task> ListAsync(TFilter filter, TOptions options, CancellationToken cancellationToken = default) { - var models = await _entity.ListAsync(filter, options); + var models = await _entity.ListAsync(filter, options, cancellationToken); if (models == null) return default; return models.Select(Instance); } - public virtual Task> ListAsync(TFilter filter, TOptions options) - => _entity.ListAsync(filter, options); + public virtual Task> ListAsync(TFilter filter, TOptions options, CancellationToken cancellationToken = default) + => _entity.ListAsync(filter, options, cancellationToken); #endregion @@ -104,21 +105,21 @@ public virtual Task> ListAsync(TFilter filter, TOptions option #region Max - public Task MaxAsync(string columnName) where TResult : struct - => MaxAsync(columnName, null); + public Task MaxAsync(string columnName, CancellationToken cancellationToken = default) where TResult : struct + => MaxAsync(columnName, null, cancellationToken); - public virtual Task MaxAsync(string columName, Action filterSetup) where TResult : struct - => _entity.MaxAsync(columName, filterSetup); + public virtual Task MaxAsync(string columName, Action filterSetup, CancellationToken cancellationToken = default) where TResult : struct + => _entity.MaxAsync(columName, filterSetup, cancellationToken); #endregion #region Min - public Task MinAsync(string columnName) where TResult : struct - => MinAsync(columnName, null); + public Task MinAsync(string columnName, CancellationToken cancellationToken = default) where TResult : struct + => MinAsync(columnName, null, cancellationToken); - public virtual Task MinAsync(string columName, Action filterSetup) where TResult : struct - => _entity.MinAsync(columName, filterSetup); + public virtual Task MinAsync(string columName, Action filterSetup, CancellationToken cancellationToken = default) where TResult : struct + => _entity.MinAsync(columName, filterSetup, cancellationToken); #endregion @@ -126,31 +127,31 @@ public virtual Task> ListAsync(TFilter filter, TOptions option #region IDelete - public virtual Task<(int Deleted, int Added)> ReplaceAsync(Action filterSetup, IEnumerable models) - => _entity.ReplaceAsync(filterSetup, models.Select(x => x.Model)); + public virtual Task<(int Deleted, int Added)> ReplaceAsync(Action filterSetup, IEnumerable models, CancellationToken cancellationToken = default) + => _entity.ReplaceAsync(filterSetup, models.Select(x => x.Model), cancellationToken); #endregion #region IUpdate - public virtual Task UpdateAsync(Action updateSetup, Action filterSetup = null, Action optionsSetup = null) - => _entity.UpdateAsync(updateSetup, filterSetup, optionsSetup); + public virtual Task UpdateAsync(Action updateSetup, Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) + => _entity.UpdateAsync(updateSetup, filterSetup, optionsSetup, cancellationToken); - public Task UpdateAsync(TProjection model) - => UpdateAsync(new[] { model }); + public Task UpdateAsync(TProjection model, CancellationToken cancellationToken = default) + => UpdateAsync(new[] { model }, cancellationToken); - public virtual Task UpdateAsync(IEnumerable models) - => _entity.UpdateAsync(models.Select(x => x.Model)); + public virtual Task UpdateAsync(IEnumerable models, CancellationToken cancellationToken = default) + => _entity.UpdateAsync(models.Select(x => x.Model), cancellationToken); #endregion #region IUpsert - public Task UpsertAsync(TProjection model) - => UpsertAsync(new[] { model }); + public Task UpsertAsync(TProjection model, CancellationToken cancellationToken = default) + => UpsertAsync(new[] { model }, cancellationToken); - public virtual Task UpsertAsync(IEnumerable models) - => _entity.UpsertAsync(models.Select(x => x.Model)); + public virtual Task UpsertAsync(IEnumerable models, CancellationToken cancellationToken = default) + => _entity.UpsertAsync(models.Select(x => x.Model), cancellationToken); #endregion diff --git a/src/Simpleverse.Repository/Operations/IAdd.cs b/src/Simpleverse.Repository/Operations/IAdd.cs index 7322c47..fb9a698 100644 --- a/src/Simpleverse.Repository/Operations/IAdd.cs +++ b/src/Simpleverse.Repository/Operations/IAdd.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Operations @@ -6,7 +7,7 @@ namespace Simpleverse.Repository.Operations public interface IAdd where T : class { - Task AddAsync(T model); - Task AddAsync(IEnumerable models); + Task AddAsync(T model, CancellationToken cancellationToken = default); + Task AddAsync(IEnumerable models, CancellationToken cancellationToken = default); } } diff --git a/src/Simpleverse.Repository/Operations/IAggregate.cs b/src/Simpleverse.Repository/Operations/IAggregate.cs index 8cbfa30..0d323e0 100644 --- a/src/Simpleverse.Repository/Operations/IAggregate.cs +++ b/src/Simpleverse.Repository/Operations/IAggregate.cs @@ -1,23 +1,24 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Operations { public interface IAggregate { - Task MaxAsync(string columnName) + Task MaxAsync(string columnName, CancellationToken cancellationToken = default) where TResult : struct; - Task MinAsync(string columnName) + Task MinAsync(string columnName, CancellationToken cancellationToken = default) where TResult : struct; } public interface IAggregate : IAggregate { - Task MaxAsync(string columnName, Action filterSetup = null) + Task MaxAsync(string columnName, Action filterSetup = null, CancellationToken cancellationToken = default) where TResult : struct; - Task MinAsync(string columnName, Action filterSetup = null) + Task MinAsync(string columnName, Action filterSetup = null, CancellationToken cancellationToken = default) where TResult : struct; } } diff --git a/src/Simpleverse.Repository/Operations/IDelete.cs b/src/Simpleverse.Repository/Operations/IDelete.cs index 2f308e5..9d729e4 100644 --- a/src/Simpleverse.Repository/Operations/IDelete.cs +++ b/src/Simpleverse.Repository/Operations/IDelete.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Operations @@ -7,8 +8,8 @@ namespace Simpleverse.Repository.Operations public interface IDelete where T : class { - Task DeleteAsync(T model); - Task DeleteAsync(IEnumerable models); + Task DeleteAsync(T model, CancellationToken cancellationToken = default); + Task DeleteAsync(IEnumerable models, CancellationToken cancellationToken = default); } public interface IDelete : IDelete @@ -16,6 +17,6 @@ public interface IDelete : IDelete where TFilter : class where TOptions : class { - Task DeleteAsync(Action filterSetup = null, Action optionsSetup = null); + Task DeleteAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/Simpleverse.Repository/Operations/IQueryExist.cs b/src/Simpleverse.Repository/Operations/IQueryExist.cs index 489e883..051c428 100644 --- a/src/Simpleverse.Repository/Operations/IQueryExist.cs +++ b/src/Simpleverse.Repository/Operations/IQueryExist.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Operations @@ -6,6 +7,6 @@ namespace Simpleverse.Repository.Operations public interface IQueryExist where TFilter : class { - Task ExistsAsync(Action filterSetup = null); + Task ExistsAsync(Action filterSetup = null, CancellationToken cancellationToken = default); } } diff --git a/src/Simpleverse.Repository/Operations/IQueryGet.cs b/src/Simpleverse.Repository/Operations/IQueryGet.cs index 73360aa..521a405 100644 --- a/src/Simpleverse.Repository/Operations/IQueryGet.cs +++ b/src/Simpleverse.Repository/Operations/IQueryGet.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Operations @@ -8,8 +9,8 @@ public interface IQueryGet where TFilter : class where TOptions : class { - Task GetAsync(Action filterSetup = null, Action optionsSetup = null); + Task GetAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default); - Task GetAsync(Action filterSetup = null, Action optionsSetup = null); + Task GetAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default); } } diff --git a/src/Simpleverse.Repository/Operations/IQueryList.cs b/src/Simpleverse.Repository/Operations/IQueryList.cs index de62b7d..ead3b23 100644 --- a/src/Simpleverse.Repository/Operations/IQueryList.cs +++ b/src/Simpleverse.Repository/Operations/IQueryList.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Operations @@ -9,12 +10,12 @@ public interface IQueryList where TFilter : class where TOptions : class { - Task> ListAsync(Action filterSetup = null, Action optionsSetup = null); + Task> ListAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default); - Task> ListAsync(Action filterSetup = null, Action optionsSetup = null); + Task> ListAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default); - Task> ListAsync(TFilter filter, TOptions options); + Task> ListAsync(TFilter filter, TOptions options, CancellationToken cancellationToken = default); - Task> ListAsync(TFilter filter, TOptions options); + Task> ListAsync(TFilter filter, TOptions options, CancellationToken cancellationToken = default); } } diff --git a/src/Simpleverse.Repository/Operations/IReplace.cs b/src/Simpleverse.Repository/Operations/IReplace.cs index 10b0b35..b630960 100644 --- a/src/Simpleverse.Repository/Operations/IReplace.cs +++ b/src/Simpleverse.Repository/Operations/IReplace.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Operations @@ -10,7 +11,8 @@ public interface IReplace { Task<(int Deleted, int Added)> ReplaceAsync( Action filterSetup, - IEnumerable models + IEnumerable models, + CancellationToken cancellationToken = default ); } } diff --git a/src/Simpleverse.Repository/Operations/IUpdate.cs b/src/Simpleverse.Repository/Operations/IUpdate.cs index b15da14..701890e 100644 --- a/src/Simpleverse.Repository/Operations/IUpdate.cs +++ b/src/Simpleverse.Repository/Operations/IUpdate.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Operations @@ -7,8 +8,8 @@ namespace Simpleverse.Repository.Operations public interface IUpdate where T : class { - Task UpdateAsync(T model); - Task UpdateAsync(IEnumerable models); + Task UpdateAsync(T model, CancellationToken cancellationToken = default); + Task UpdateAsync(IEnumerable models, CancellationToken cancellationToken = default); } public interface IUpdate @@ -16,6 +17,6 @@ public interface IUpdate where TFilter : class where TOptions : class { - Task UpdateAsync(Action updateSetup, Action filterSetup = null, Action optionsSetup = null); + Task UpdateAsync(Action updateSetup, Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default); } } diff --git a/src/Simpleverse.Repository/Operations/IUpsert.cs b/src/Simpleverse.Repository/Operations/IUpsert.cs index 6bf5099..37a0bb8 100644 --- a/src/Simpleverse.Repository/Operations/IUpsert.cs +++ b/src/Simpleverse.Repository/Operations/IUpsert.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Simpleverse.Repository.Operations @@ -6,7 +7,7 @@ namespace Simpleverse.Repository.Operations public interface IUpsert : IAdd, IUpdate where T : class { - Task UpsertAsync(T model); - Task UpsertAsync(IEnumerable models); + Task UpsertAsync(T model, CancellationToken cancellationToken = default); + Task UpsertAsync(IEnumerable models, CancellationToken cancellationToken = default); } } From f2156f0b7b4af19e21cf424fd33d8e94fe599c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Klari=C4=87?= Date: Tue, 31 Mar 2026 10:10:57 +0200 Subject: [PATCH 2/3] fix: Remove CancellationToken from single-model DeleteAsync Dapper.Contrib's DeleteAsync does not support CancellationToken. The single-model delete overloads always route to Dapper.Contrib and can never honour the token, so the parameter is removed. IEnumerable and filter-based delete variants are unaffected as they use bulk operations (SqlRepository path) that do support it. Co-Authored-By: Claude Sonnet 4.6 --- src/Simpleverse.Repository.Db/Entity/Entity.cs | 4 ++-- src/Simpleverse.Repository.Db/Entity/ProjectedEntity.cs | 4 ++-- src/Simpleverse.Repository.Db/Operations/IDeleteDb.cs | 2 +- src/Simpleverse.Repository/Entity/ProjectedEntity.cs | 4 ++-- src/Simpleverse.Repository/Operations/IDelete.cs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Simpleverse.Repository.Db/Entity/Entity.cs b/src/Simpleverse.Repository.Db/Entity/Entity.cs index 3508c54..6562e90 100644 --- a/src/Simpleverse.Repository.Db/Entity/Entity.cs +++ b/src/Simpleverse.Repository.Db/Entity/Entity.cs @@ -373,11 +373,11 @@ public virtual Task UpsertAsync( #region ByModel - public async Task DeleteAsync(TModel model, CancellationToken cancellationToken = default) + public async Task DeleteAsync(TModel model) { return await Repository.ExecuteAsync((conn) => DeleteAsync(conn, model)); } - public virtual Task DeleteAsync(IDbConnection connection, TModel model, IDbTransaction transaction = null, CancellationToken cancellationToken = default) + public virtual Task DeleteAsync(IDbConnection connection, TModel model, IDbTransaction transaction = null) => connection.DeleteAsync(model, transaction: transaction); public async Task DeleteAsync(IEnumerable models, CancellationToken cancellationToken = default) diff --git a/src/Simpleverse.Repository.Db/Entity/ProjectedEntity.cs b/src/Simpleverse.Repository.Db/Entity/ProjectedEntity.cs index 41ef9cf..8d637ba 100644 --- a/src/Simpleverse.Repository.Db/Entity/ProjectedEntity.cs +++ b/src/Simpleverse.Repository.Db/Entity/ProjectedEntity.cs @@ -54,8 +54,8 @@ public override sealed Task DeleteAsync(Action filterSetup = null, public virtual Task DeleteAsync(IDbConnection connection, Action filterSetup = null, Action optionsSetup = null, IDbTransaction transaction = null, CancellationToken cancellationToken = default) => _entity.DeleteAsync(connection, filterSetup, optionsSetup, transaction: transaction, cancellationToken: cancellationToken); - public async Task DeleteAsync(IDbConnection connection, TProjection model, IDbTransaction transaction = null, CancellationToken cancellationToken = default) - => await DeleteAsync(connection, new[] { model }, transaction, cancellationToken) > 0; + public async Task DeleteAsync(IDbConnection connection, TProjection model, IDbTransaction transaction = null) + => await DeleteAsync(connection, new[] { model }, transaction) > 0; public override sealed Task DeleteAsync(IEnumerable models, CancellationToken cancellationToken = default) => _entity.ExecuteAsyncWithTransaction((conn, tran) => DeleteAsync(conn, models, tran, cancellationToken)); diff --git a/src/Simpleverse.Repository.Db/Operations/IDeleteDb.cs b/src/Simpleverse.Repository.Db/Operations/IDeleteDb.cs index 07403d2..4bf309c 100644 --- a/src/Simpleverse.Repository.Db/Operations/IDeleteDb.cs +++ b/src/Simpleverse.Repository.Db/Operations/IDeleteDb.cs @@ -10,7 +10,7 @@ namespace Simpleverse.Repository.Db.Operations public interface IDeleteDb : IDelete where T : class { - Task DeleteAsync(IDbConnection connection, T model, IDbTransaction transaction = null, CancellationToken cancellationToken = default); + Task DeleteAsync(IDbConnection connection, T model, IDbTransaction transaction = null); Task DeleteAsync(IDbConnection connection, IEnumerable models, IDbTransaction transaction = null, CancellationToken cancellationToken = default); } diff --git a/src/Simpleverse.Repository/Entity/ProjectedEntity.cs b/src/Simpleverse.Repository/Entity/ProjectedEntity.cs index 8f6441b..054bcca 100644 --- a/src/Simpleverse.Repository/Entity/ProjectedEntity.cs +++ b/src/Simpleverse.Repository/Entity/ProjectedEntity.cs @@ -48,8 +48,8 @@ public virtual Task AddAsync(IEnumerable models, CancellationT public virtual Task DeleteAsync(Action filterSetup = null, Action optionsSetup = null, CancellationToken cancellationToken = default) => _entity.DeleteAsync(filterSetup, optionsSetup, cancellationToken); - public async Task DeleteAsync(TProjection model, CancellationToken cancellationToken = default) - => await DeleteAsync(new[] { model }, cancellationToken) > 0; + public async Task DeleteAsync(TProjection model) + => await DeleteAsync(new[] { model }) > 0; public virtual Task DeleteAsync(IEnumerable models, CancellationToken cancellationToken = default) => _entity.DeleteAsync(models.Select(x => x.Model), cancellationToken); diff --git a/src/Simpleverse.Repository/Operations/IDelete.cs b/src/Simpleverse.Repository/Operations/IDelete.cs index 9d729e4..7a8c89c 100644 --- a/src/Simpleverse.Repository/Operations/IDelete.cs +++ b/src/Simpleverse.Repository/Operations/IDelete.cs @@ -8,7 +8,7 @@ namespace Simpleverse.Repository.Operations public interface IDelete where T : class { - Task DeleteAsync(T model, CancellationToken cancellationToken = default); + Task DeleteAsync(T model); Task DeleteAsync(IEnumerable models, CancellationToken cancellationToken = default); } From f03410e1ef395b64616c0100adb58bd074e9898e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Klari=C4=87?= Date: Tue, 31 Mar 2026 11:37:27 +0200 Subject: [PATCH 3/3] fix: Properly thread CancellationToken through bulk operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Downgrade Microsoft.Data.SqlClient from 7.0.0 to 5.2.2 — v7 dropped .NET 6.0 support, causing SqlConnection.get_State() to throw NullReferenceException on the test project's net6.0 target. Forward CancellationToken through the entire BulkExtensions call chain: InsertBulkAsync/UpdateBulkAsync/DeleteBulkAsync -> ExecuteAsync -> BulkSourceAsync -> TransferBulkAsync -> CreateTemporaryTableFromTable and ExecuteWithOutputMapAsync. Previously the token was accepted in every signature but silently dropped before reaching any Dapper CommandDefinition, so pre-cancelled tokens had no effect. Co-Authored-By: Claude Sonnet 4.6 --- .../Simpleverse.Repository.Db.csproj | 2 +- .../SqlServer/BulkExtensions.cs | 40 ++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/Simpleverse.Repository.Db/Simpleverse.Repository.Db.csproj b/src/Simpleverse.Repository.Db/Simpleverse.Repository.Db.csproj index 1f6a255..c41f687 100644 --- a/src/Simpleverse.Repository.Db/Simpleverse.Repository.Db.csproj +++ b/src/Simpleverse.Repository.Db/Simpleverse.Repository.Db.csproj @@ -45,7 +45,7 @@ - + diff --git a/src/Simpleverse.Repository.Db/SqlServer/BulkExtensions.cs b/src/Simpleverse.Repository.Db/SqlServer/BulkExtensions.cs index 8782649..9dcf6a0 100644 --- a/src/Simpleverse.Repository.Db/SqlServer/BulkExtensions.cs +++ b/src/Simpleverse.Repository.Db/SqlServer/BulkExtensions.cs @@ -32,7 +32,8 @@ public static async Task TransferBulkAsync( meta.TableName, meta.Properties, transaction: transaction, - sqlBulkCopy: sqlBulkCopy + sqlBulkCopy: sqlBulkCopy, + cancellationToken: cancellationToken ); } @@ -63,7 +64,7 @@ public static async Task TransferBulkAsync( if (connection.State != ConnectionState.Open) throw new ArgumentException("Connection is required to be opened by the calling code."); - var insertedTableName = await connection.CreateTemporaryTableFromTable(tableName, columnsToCopy, transaction); + var insertedTableName = await connection.CreateTemporaryTableFromTable(tableName, columnsToCopy, transaction, cancellationToken: cancellationToken); if (columnsToCopy.Count() * entitiesToInsert.Count() < 2000 || !(connection is SqlConnection)) { @@ -83,9 +84,7 @@ await connection.ExecuteAsyncWithTransaction( "; await connection.ExecuteAsync( - query.ToString(), - parameters, - transaction: tran + new CommandDefinition(query.ToString(), parameters, transaction: tran, cancellationToken: cancellationToken) ); } @@ -100,7 +99,7 @@ await connection.ExecuteAsync( { sqlBulkCopy?.Invoke(bulkCopy); bulkCopy.DestinationTableName = insertedTableName; - await bulkCopy.WriteToServerAsync(ToDataTable(entitiesToInsert, columnsToCopy).CreateDataReader()); + await bulkCopy.WriteToServerAsync(ToDataTable(entitiesToInsert, columnsToCopy).CreateDataReader(), cancellationToken); } } @@ -272,7 +271,8 @@ public async static Task InsertBulkAsync( var outputTarget = await conn.CreateTemporaryTableFromTable( typeMeta.TableName, typeMeta.PropertiesKeyAndExplicit, - transaction + transaction, + cancellationToken: cancellationToken ); outputSource = outputTarget; @@ -296,11 +296,13 @@ public async static Task InsertBulkAsync( ); }, transaction, - commandTimeout + commandTimeout, + cancellationToken: cancellationToken ); }, transaction: transaction, - sqlBulkCopy: sqlBulkCopy + sqlBulkCopy: sqlBulkCopy, + cancellationToken: cancellationToken ); } @@ -366,11 +368,13 @@ INNER JOIN {typeMeta.TableName} AS Target ); }, transaction, - commandTimeout + commandTimeout, + cancellationToken: cancellationToken ); }, transaction: transaction, - sqlBulkCopy: sqlBulkCopy + sqlBulkCopy: sqlBulkCopy, + cancellationToken: cancellationToken ); } @@ -417,14 +421,12 @@ INNER JOIN {typeMeta.TableName} AS Target "; return await connection.ExecuteAsync( - query, - param: parameters, - commandTimeout: commandTimeout, - transaction: transaction + new CommandDefinition(query, parameters, transaction: transaction, commandTimeout: commandTimeout, cancellationToken: cancellationToken) ); }, transaction: transaction, - sqlBulkCopy: sqlBulkCopy + sqlBulkCopy: sqlBulkCopy, + cancellationToken: cancellationToken ); return result; } @@ -475,7 +477,8 @@ AS SourceInner ( typeMeta.TableName, properties, transaction: transaction, - sqlBulkCopy: sqlBulkCopy + sqlBulkCopy: sqlBulkCopy, + cancellationToken: cancellationToken ); return (insertedTableName, null); @@ -498,7 +501,8 @@ public static async Task ExecuteAsync( entities, properties, transaction: transaction, - sqlBulkCopy: sqlBulkCopy + sqlBulkCopy: sqlBulkCopy, + cancellationToken: cancellationToken ); return await executor(connection, source, parameters, properties);