@@ -33,12 +33,16 @@ public void EnlistInSystemTransactionIfNeeded(ISessionImplementor session)
33
33
// Ensure the session does not run on a thread supposed to be blocked, waiting
34
34
// for transaction completion.
35
35
session . TransactionContext ? . WaitOne ( ) ;
36
-
36
+
37
37
var transaction = System . Transactions . Transaction . Current ;
38
- // We may have defined the transaction context before having the connection, do not
39
- // move under test on TransactionContext already defined. Otherwise we would rely on
40
- // connection auto-enlistment setting.
41
- session . ConnectionManager . EnlistIfRequired ( transaction ) ;
38
+ // We may have defined the transaction context before having the connection, so we
39
+ // need to ensure enlistment even when the transaction context is already defined.
40
+ // But avoid redefining to a sub-scope transaction.
41
+ if ( session . TransactionContext == null ||
42
+ ( ( SystemTransactionContext ) session . TransactionContext ) . AmbientTransaction == transaction )
43
+ {
44
+ session . ConnectionManager . EnlistIfRequired ( transaction ) ;
45
+ }
42
46
43
47
if ( session . TransactionContext != null || transaction == null )
44
48
{
@@ -49,11 +53,11 @@ public void EnlistInSystemTransactionIfNeeded(ISessionImplementor session)
49
53
session . TransactionContext = transactionContext ;
50
54
_logger . DebugFormat (
51
55
"enlisted into DTC transaction: {0}" ,
52
- transactionContext . AmbientTransation . IsolationLevel ) ;
56
+ transactionContext . AmbientTransaction . IsolationLevel ) ;
53
57
session . AfterTransactionBegin ( null ) ;
54
58
55
- transactionContext . AmbientTransation . TransactionCompleted += transactionContext . TransactionCompleted ;
56
- transactionContext . AmbientTransation . EnlistVolatile (
59
+ transactionContext . AmbientTransaction . TransactionCompleted += transactionContext . TransactionCompleted ;
60
+ transactionContext . AmbientTransaction . EnlistVolatile (
57
61
transactionContext ,
58
62
EnlistmentOptions . EnlistDuringPrepareRequired ) ;
59
63
}
@@ -74,30 +78,30 @@ public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork wo
74
78
75
79
public class SystemTransactionContext : ITransactionContext , IEnlistmentNotification
76
80
{
77
- internal System . Transactions . Transaction AmbientTransation { get ; private set ; }
81
+ internal System . Transactions . Transaction AmbientTransaction { get ; private set ; }
78
82
public bool ShouldCloseSessionOnSystemTransactionCompleted { get ; set ; }
79
83
public bool IsInActiveTransaction { get ; internal set ; }
80
84
81
85
private readonly ISessionImplementor _sessionImplementor ;
82
86
private readonly ManualResetEvent _waitEvent = new ManualResetEvent ( true ) ;
83
87
private volatile bool _locked ;
84
88
private readonly AsyncLocal < bool > _bypassWait = new AsyncLocal < bool > ( ) ;
85
- private bool IsDistributed => AmbientTransation . TransactionInformation . DistributedIdentifier != Guid . Empty ;
89
+ private bool IsDistributed => AmbientTransaction . TransactionInformation . DistributedIdentifier != Guid . Empty ;
86
90
87
91
public SystemTransactionContext (
88
92
ISessionImplementor sessionImplementor ,
89
93
System . Transactions . Transaction transaction )
90
94
{
91
95
_sessionImplementor = sessionImplementor ;
92
- AmbientTransation = transaction . Clone ( ) ;
96
+ AmbientTransaction = transaction . Clone ( ) ;
93
97
IsInActiveTransaction = true ;
94
98
}
95
99
96
100
public void WaitOne ( )
97
101
{
98
102
if ( _bypassWait . Value || _isDisposed )
99
103
return ;
100
- if ( ! _locked && AmbientTransation . TransactionInformation . Status != TransactionStatus . Active )
104
+ if ( ! _locked && AmbientTransaction . TransactionInformation . Status != TransactionStatus . Active )
101
105
// Rollback case may end the transaction without a prepare phase, apply the lock.
102
106
SafeLockSession ( ) ;
103
107
try
@@ -157,7 +161,7 @@ void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
157
161
{
158
162
try
159
163
{
160
- using ( var tx = new TransactionScope ( AmbientTransation ) )
164
+ using ( var tx = new TransactionScope ( AmbientTransaction ) )
161
165
{
162
166
if ( _sessionImplementor . FlushMode != FlushMode . Manual && _sessionImplementor . ConnectionManager . IsConnected )
163
167
{
@@ -197,9 +201,6 @@ void IEnlistmentNotification.InDoubt(Enlistment enlistment)
197
201
198
202
private void ProcessSecondPhase ( Enlistment enlistment , bool ? success )
199
203
{
200
- // In case of rollback, the prepare phase may have not be run, and we then need to lock as soon as possible.
201
- // There is no guarantee the second phase will be run before the completed event, doing that at both places.
202
- SafeLockSession ( ) ;
203
204
using ( new SessionIdLoggingContext ( _sessionImplementor . SessionId ) )
204
205
{
205
206
_logger . Debug (
@@ -225,9 +226,6 @@ private void ProcessSecondPhase(Enlistment enlistment, bool? success)
225
226
226
227
public void TransactionCompleted ( object sender , TransactionEventArgs e )
227
228
{
228
- // In case of rollback, the prepare phase may have not be run, and we then need to lock as soon as possible.
229
- // There is no guarantee the second phase will be run before the completed event, doing that at both places.
230
- SafeLockSession ( ) ;
231
229
e . Transaction . TransactionCompleted -= TransactionCompleted ;
232
230
// This event may execute before second phase, so we cannot try to get the success from second phase.
233
231
// Using this event is required by example in case the prepare phase failed and called force rollback:
@@ -237,7 +235,7 @@ public void TransactionCompleted(object sender, TransactionEventArgs e)
237
235
try
238
236
{
239
237
wasSuccessful =
240
- e . Transaction . TransactionInformation . Status == TransactionStatus . Committed ;
238
+ AmbientTransaction . TransactionInformation . Status == TransactionStatus . Committed ;
241
239
}
242
240
catch ( ObjectDisposedException ode )
243
241
{
@@ -253,6 +251,7 @@ private void RunAfterTransactionActions(bool wasSuccessful)
253
251
if ( _afterTransactionActionsDone )
254
252
// Probably called from In-Doubt and TransactionCompleted.
255
253
return ;
254
+ _afterTransactionActionsDone = true ;
256
255
// Allow transaction completed actions to run while others stay blocked.
257
256
_bypassWait . Value = true ;
258
257
try
@@ -272,7 +271,6 @@ private void RunAfterTransactionActions(bool wasSuccessful)
272
271
}
273
272
finally
274
273
{
275
- _afterTransactionActionsDone = true ;
276
274
// Dispose releases blocked threads by the way.
277
275
// Must dispose in case !ShouldCloseSessionOnSystemTransactionCompleted, since
278
276
// we nullify session TransactionContext, causing it to have nothing still holding it.
@@ -296,11 +294,8 @@ protected virtual void Dispose(bool disposing)
296
294
{
297
295
if ( disposing )
298
296
{
299
- if ( AmbientTransation != null )
300
- {
301
- AmbientTransation . Dispose ( ) ;
302
- AmbientTransation = null ;
303
- }
297
+ AmbientTransaction ? . Dispose ( ) ;
298
+ AmbientTransaction = null ;
304
299
_waitEvent . Set ( ) ;
305
300
_waitEvent . Dispose ( ) ;
306
301
}
0 commit comments