Skip to content

Commit 90a2bfe

Browse files
NH-4011 - Move from transaction completion event to second phase notifications.
1 parent 913a848 commit 90a2bfe

File tree

1 file changed

+25
-29
lines changed

1 file changed

+25
-29
lines changed

src/NHibernate/Transaction/AdoNetWithSystemTransactionFactory.cs

+25-29
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)