@@ -206,7 +206,6 @@ public SystemTransactionContext(
206206 _session = session ?? throw new ArgumentNullException ( nameof ( session ) ) ;
207207 _originalTransaction = transaction ?? throw new ArgumentNullException ( nameof ( transaction ) ) ;
208208 EnlistedTransaction = transaction . Clone ( ) ;
209- EnlistedTransaction . TransactionCompleted += TransactionCompleted ;
210209 _systemTransactionCompletionLockTimeout = systemTransactionCompletionLockTimeout ;
211210 _useConnectionOnSystemTransactionPrepare = useConnectionOnSystemTransactionPrepare ;
212211 }
@@ -342,23 +341,24 @@ public virtual void Prepare(PreparingEnlistment preparingEnlistment)
342341 // the transaction scope disposal.
343342 Lock ( ) ;
344343
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 ( ) ;
351346 }
352347 catch ( Exception exception )
353348 {
354349 _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+ }
357358 }
358359 }
359360 }
360361
361- // With Done call above, should never be called.
362362 void IEnlistmentNotification . Commit ( Enlistment enlistment )
363363 => ProcessSecondPhase ( enlistment , true ) ;
364364
@@ -367,7 +367,6 @@ void IEnlistmentNotification.Commit(Enlistment enlistment)
367367 void IEnlistmentNotification . Rollback ( Enlistment enlistment )
368368 => ProcessSecondPhase ( enlistment , false ) ;
369369
370- // With Done call above, should never be called.
371370 void IEnlistmentNotification . InDoubt ( Enlistment enlistment )
372371 => ProcessSecondPhase ( enlistment , null ) ;
373372
@@ -387,34 +386,32 @@ protected virtual void ProcessSecondPhase(Enlistment enlistment, bool? success)
387386 ? "Committing system transaction"
388387 : "Rolled back system transaction"
389388 : "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.
393389
394- enlistment . Done ( ) ;
390+ try
391+ {
392+ CompleteTransaction ( success ?? false ) ;
393+ }
394+ finally
395+ {
396+ enlistment . Done ( ) ;
397+ }
395398 }
396399 }
397400
398401 #endregion
399402
400403 /// <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
402405 /// transaction. Notify end of transaction to the session and to <see cref="ConnectionManager.DependentSessions"/>
403406 /// if any. Close sessions requiring it then cleanup transaction contextes and then <see cref="Unlock"/> blocked
404407 /// threads.
405408 /// </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 )
410412 {
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.
415413 try
416414 {
417- EnlistedTransaction . TransactionCompleted -= TransactionCompleted ;
418415 // Allow transaction completed actions to run while others stay blocked.
419416 _bypassLock . Value = true ;
420417 using ( new SessionIdLoggingContext ( _session . SessionId ) )
@@ -436,11 +433,10 @@ protected virtual void TransactionCompleted(object sender, TransactionEventArgs
436433 // within scopes, although mixing is not advised.
437434 if ( ! ShouldCloseSessionOnSystemTransactionCompleted )
438435 _session . ConnectionManager . EnlistIfRequired ( null ) ;
439-
440- var wasSuccessful = GetTransactionStatus ( ) == TransactionStatus . Committed ;
441- _session . AfterTransactionCompletion ( wasSuccessful , null ) ;
436+
437+ _session . AfterTransactionCompletion ( isCommitted , null ) ;
442438 foreach ( var dependentSession in _session . ConnectionManager . DependentSessions )
443- dependentSession . AfterTransactionCompletion ( wasSuccessful , null ) ;
439+ dependentSession . AfterTransactionCompletion ( isCommitted , null ) ;
444440
445441 Cleanup ( _session ) ;
446442 }
0 commit comments