@@ -206,7 +206,6 @@ public SystemTransactionContext(
206
206
_session = session ?? throw new ArgumentNullException ( nameof ( session ) ) ;
207
207
_originalTransaction = transaction ?? throw new ArgumentNullException ( nameof ( transaction ) ) ;
208
208
EnlistedTransaction = transaction . Clone ( ) ;
209
- EnlistedTransaction . TransactionCompleted += TransactionCompleted ;
210
209
_systemTransactionCompletionLockTimeout = systemTransactionCompletionLockTimeout ;
211
210
_useConnectionOnSystemTransactionPrepare = useConnectionOnSystemTransactionPrepare ;
212
211
}
@@ -342,23 +341,24 @@ public virtual void Prepare(PreparingEnlistment preparingEnlistment)
342
341
// the transaction scope disposal.
343
342
Lock ( ) ;
344
343
345
- _logger . Debug ( "Prepared and done for system transaction" ) ;
346
- // We do not have anything more to do in second phases callbacks: the remaining work is handled in
347
- // transaction completion event, which always executes, contrary to second phases callbacks which
348
- // do not, depending on the rollback cases.
349
- // This saves a thread when distributed.
350
- preparingEnlistment . Done ( ) ;
344
+ _logger . Debug ( "Prepared for system transaction" ) ;
345
+ preparingEnlistment . Prepared ( ) ;
351
346
}
352
347
catch ( Exception exception )
353
348
{
354
349
_logger . Error ( "System transaction prepare phase failed" , exception ) ;
355
- Lock ( ) ;
356
- preparingEnlistment . ForceRollback ( exception ) ;
350
+ try
351
+ {
352
+ CompleteTransaction ( false ) ;
353
+ }
354
+ finally
355
+ {
356
+ preparingEnlistment . ForceRollback ( exception ) ;
357
+ }
357
358
}
358
359
}
359
360
}
360
361
361
- // With Done call above, should never be called.
362
362
void IEnlistmentNotification . Commit ( Enlistment enlistment )
363
363
=> ProcessSecondPhase ( enlistment , true ) ;
364
364
@@ -367,7 +367,6 @@ void IEnlistmentNotification.Commit(Enlistment enlistment)
367
367
void IEnlistmentNotification . Rollback ( Enlistment enlistment )
368
368
=> ProcessSecondPhase ( enlistment , false ) ;
369
369
370
- // With Done call above, should never be called.
371
370
void IEnlistmentNotification . InDoubt ( Enlistment enlistment )
372
371
=> ProcessSecondPhase ( enlistment , null ) ;
373
372
@@ -387,34 +386,32 @@ protected virtual void ProcessSecondPhase(Enlistment enlistment, bool? success)
387
386
? "Committing system transaction"
388
387
: "Rolled back system transaction"
389
388
: "System transaction is in doubt" ) ;
390
- // we have not much to do here, since it is the actual
391
- // DB connection that will commit/rollback the transaction
392
- // After transaction actions are raised from TransactionCompleted event.
393
389
394
- enlistment . Done ( ) ;
390
+ try
391
+ {
392
+ CompleteTransaction ( success ?? false ) ;
393
+ }
394
+ finally
395
+ {
396
+ enlistment . Done ( ) ;
397
+ }
395
398
}
396
399
}
397
400
398
401
#endregion
399
402
400
403
/// <summary>
401
- /// Handle the transaction completion event . Notify <see cref="ConnectionManager"/> of the end of the
404
+ /// Handle the transaction completion. Notify <see cref="ConnectionManager"/> of the end of the
402
405
/// transaction. Notify end of transaction to the session and to <see cref="ConnectionManager.DependentSessions"/>
403
406
/// if any. Close sessions requiring it then cleanup transaction contextes and then <see cref="Unlock"/> blocked
404
407
/// threads.
405
408
/// </summary>
406
- /// <param name="sender">The object issuing the event.</param>
407
- /// <param name="e">The event argument. <see cref="TransactionEventArgs.Transaction"/> is not guaranteed to
408
- /// be the transaction on which the event was set, especially when it was set on a clone.</param>
409
- protected virtual void TransactionCompleted ( object sender , TransactionEventArgs e )
409
+ /// <param name="isCommitted"><see langword="true"/> if the transaction is committed, <see langword="false"/>
410
+ /// otherwise.</param>
411
+ protected virtual void CompleteTransaction ( bool isCommitted )
410
412
{
411
- // This event may execute before second phase, so we cannot try to get the success from second phase.
412
- // Using this event is required by example in case the prepare phase failed and called force rollback:
413
- // no second phase would occur for this ressource. Maybe this may happen in some other circumstances
414
- // too.
415
413
try
416
414
{
417
- EnlistedTransaction . TransactionCompleted -= TransactionCompleted ;
418
415
// Allow transaction completed actions to run while others stay blocked.
419
416
_bypassLock . Value = true ;
420
417
using ( new SessionIdLoggingContext ( _session . SessionId ) )
@@ -436,11 +433,10 @@ protected virtual void TransactionCompleted(object sender, TransactionEventArgs
436
433
// within scopes, although mixing is not advised.
437
434
if ( ! ShouldCloseSessionOnSystemTransactionCompleted )
438
435
_session . ConnectionManager . EnlistIfRequired ( null ) ;
439
-
440
- var wasSuccessful = GetTransactionStatus ( ) == TransactionStatus . Committed ;
441
- _session . AfterTransactionCompletion ( wasSuccessful , null ) ;
436
+
437
+ _session . AfterTransactionCompletion ( isCommitted , null ) ;
442
438
foreach ( var dependentSession in _session . ConnectionManager . DependentSessions )
443
- dependentSession . AfterTransactionCompletion ( wasSuccessful , null ) ;
439
+ dependentSession . AfterTransactionCompletion ( isCommitted , null ) ;
444
440
445
441
Cleanup ( _session ) ;
446
442
}
0 commit comments