Repository navigation
Expand file tree
/
Copy pathDatabaseClientBase.Transactions.cs
More file actions
137 lines (128 loc) · 5.24 KB
/
Copy pathDatabaseClientBase.Transactions.cs
File metadata and controls
137 lines (128 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_0_OR_GREATER
using System.Runtime.CompilerServices;
#endif
namespace DBAClientX;
public abstract partial class DatabaseClientBase
{
/// <summary>
/// Attempts to roll back an active transaction after an operation failure and rethrows the original exception.
/// </summary>
protected static void HandleTransactionFailure(Exception originalException, Action rollback, Func<bool> hasActiveTransaction)
{
var hadActiveTransaction = hasActiveTransaction();
Exception? rollbackException = null;
try
{
rollback();
}
catch (DbaTransactionException) when (!hadActiveTransaction)
{
rollbackException = null;
}
catch (Exception ex)
{
rollbackException = ex;
}
if (rollbackException != null)
{
throw new AggregateException("Transaction operation failed and rollback also failed.", originalException, rollbackException);
}
ExceptionDispatchInfo.Capture(originalException).Throw();
}
/// <summary>
/// Attempts to roll back an active transaction after an asynchronous operation failure and rethrows the original exception.
/// </summary>
protected static async Task HandleTransactionFailureAsync(
Exception originalException,
Func<CancellationToken, Task> rollbackAsync,
Func<bool> hasActiveTransaction,
CancellationToken cancellationToken)
{
var hadActiveTransaction = hasActiveTransaction();
Exception? rollbackException = null;
try
{
await rollbackAsync(CancellationToken.None).ConfigureAwait(false);
}
catch (DbaTransactionException) when (!hadActiveTransaction)
{
rollbackException = null;
}
catch (Exception ex)
{
rollbackException = ex;
}
if (rollbackException != null)
{
throw new AggregateException("Transaction operation failed and rollback also failed.", originalException, rollbackException);
}
ExceptionDispatchInfo.Capture(originalException).Throw();
}
/// <summary>
/// Executes an operation inside an already-configured transaction lifecycle.
/// </summary>
/// <typeparam name="TResult">The operation result type.</typeparam>
/// <param name="beginTransaction">Callback that starts the transaction.</param>
/// <param name="operation">Callback executed inside the transaction.</param>
/// <param name="commitTransaction">Callback that commits the transaction.</param>
/// <param name="rollbackTransaction">Callback that rolls back the transaction on failure.</param>
/// <param name="hasActiveTransaction">Callback that indicates whether a transaction is still active.</param>
protected static TResult ExecuteInTransaction<TResult>(
Action beginTransaction,
Func<TResult> operation,
Action commitTransaction,
Action rollbackTransaction,
Func<bool> hasActiveTransaction)
{
beginTransaction();
try
{
var result = operation();
commitTransaction();
return result;
}
catch (Exception ex)
{
HandleTransactionFailure(ex, rollbackTransaction, hasActiveTransaction);
throw;
}
}
/// <summary>
/// Executes an asynchronous operation inside an already-configured transaction lifecycle.
/// </summary>
/// <typeparam name="TResult">The operation result type.</typeparam>
/// <param name="beginTransactionAsync">Callback that starts the transaction.</param>
/// <param name="operationAsync">Callback executed inside the transaction.</param>
/// <param name="commitTransactionAsync">Callback that commits the transaction.</param>
/// <param name="rollbackTransactionAsync">Callback that rolls back the transaction on failure.</param>
/// <param name="hasActiveTransaction">Callback that indicates whether a transaction is still active.</param>
/// <param name="cancellationToken">Cancellation token for the async workflow.</param>
protected static async Task<TResult> ExecuteInTransactionAsync<TResult>(
Func<CancellationToken, Task> beginTransactionAsync,
Func<CancellationToken, Task<TResult>> operationAsync,
Func<CancellationToken, Task> commitTransactionAsync,
Func<CancellationToken, Task> rollbackTransactionAsync,
Func<bool> hasActiveTransaction,
CancellationToken cancellationToken)
{
await beginTransactionAsync(cancellationToken).ConfigureAwait(false);
try
{
var result = await operationAsync(cancellationToken).ConfigureAwait(false);
await commitTransactionAsync(cancellationToken).ConfigureAwait(false);
return result;
}
catch (Exception ex)
{
await HandleTransactionFailureAsync(ex, rollbackTransactionAsync, hasActiveTransaction, cancellationToken).ConfigureAwait(false);
throw;
}
}
}