/// <inheritdoc /> public async ValueTask RollbackAsync(CancellationToken cancellationToken = default) { try { await npgsqlTransaction.RollbackAsync(cancellationToken); } catch (Exception ex) { NpgsqlExceptions.Handle(ex); throw; } }
async ValueTask <TTransaction> DoBegin <TTrait, TTransaction> (IsolationLevel isolationLevel, CancellationToken cancellationToken, SqlAccess?access, Int32?defaultQueryTimeout, Int32 retry = 0) where TTrait : struct, IBeginTrait <TTransaction> where TTransaction : ISqlTransaction { while (true) { var npgsqlConnection = new NpgsqlConnection(connectionString); try { await npgsqlConnection.OpenAsync(cancellationToken); var npgsqlTransaction = await npgsqlConnection.BeginTransactionAsync(isolationLevel, cancellationToken); var transaction = default(TTrait).CreateTransaction( npgsqlConnection, npgsqlTransaction, isolationLevel, access ?? settings.defaultAccess, defaultQueryTimeout ?? settings.defaultQueryTimeout); if (access is { } accessValue&& accessValue != settings.defaultAccess) { await transaction.SetAccessAsync(accessValue, cancellationToken); } npgsqlConnection = null; return(transaction); } catch (NpgsqlException ex) when(ex.IsTransient) { if (++retry < beginRetryDelays.Length) { var timestamp = Environment.TickCount64; await(npgsqlConnection?.DisposeAsync() ?? default); npgsqlConnection = null; var delay = beginRetryDelays[retry] - (Int32)(Environment.TickCount64 - timestamp); if (delay > 0) { await Task.Delay(delay, cancellationToken); } else { await Task.Yield(); } } else { throw NpgsqlExceptions.MatchToSqlException(ex) !; } } catch (Exception ex) when(NpgsqlExceptions.MatchToSqlException(ex) is { } sqlEx) { throw sqlEx; }