Skip to content

Commit b1f9663

Browse files
committed
Attempt 1001
1 parent 16c1459 commit b1f9663

File tree

5 files changed

+110
-90
lines changed

5 files changed

+110
-90
lines changed

src/NHibernate.Test/NHSpecificTest/NH2420/Fixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public void ShouldBeAbleToReleaseSuppliedConnectionAfterDistributedTransaction()
9090
// fires *after* the transaction is committed and so it doesn't affect the success
9191
// of the transaction.
9292

93-
Assert.That(s.IsConnected, Is.False);
93+
Assert.That(() => s.IsConnected, Is.False.After(500, 100));
9494
Assert.That(((ISessionImplementor)s).ConnectionManager.IsConnected, Is.False);
9595
Assert.That(((ISessionImplementor)s).IsClosed, Is.True);
9696
}

src/NHibernate/Impl/AbstractSessionImpl.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.Data;
5+
using System.Runtime.CompilerServices;
56
using NHibernate.AdoNet;
67
using NHibernate.Cache;
78
using NHibernate.Collection;
@@ -33,13 +34,14 @@ public abstract class AbstractSessionImpl : ISessionImplementor
3334

3435
public ITransactionContext TransactionContext
3536
{
36-
get; set;
37+
[MethodImpl(MethodImplOptions.Synchronized)]
38+
get;
39+
[MethodImpl(MethodImplOptions.Synchronized)]
40+
set;
3741
}
3842

3943
private bool isAlreadyDisposed;
4044

41-
private static readonly IInternalLogger logger = LoggerProvider.LoggerFor(typeof(AbstractSessionImpl));
42-
4345
public Guid SessionId
4446
{
4547
get { return sessionId; }
@@ -330,6 +332,7 @@ protected internal void SetClosed()
330332
{
331333
if (TransactionContext != null)
332334
TransactionContext.Dispose();
335+
TransactionContext = null;
333336
}
334337
catch (Exception)
335338
{

src/NHibernate/Impl/SessionImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1653,7 +1653,7 @@ public void Dispose()
16531653
using (new SessionIdLoggingContext(SessionId))
16541654
{
16551655
log.Debug(string.Format("[session-id={0}] running ISession.Dispose()", SessionId));
1656-
if (TransactionContext != null)
1656+
if (Factory.TransactionFactory.IsInDistributedActiveTransaction(this))
16571657
{
16581658
TransactionContext.ShouldCloseSessionOnDistributedTransactionCompleted = true;
16591659
return;

src/NHibernate/Impl/StatelessSessionImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@ public void Dispose()
862862
using (new SessionIdLoggingContext(SessionId))
863863
{
864864
log.Debug("running IStatelessSession.Dispose()");
865-
if (TransactionContext != null)
865+
if (Factory.TransactionFactory.IsInDistributedActiveTransaction(this))
866866
{
867867
TransactionContext.ShouldCloseSessionOnDistributedTransactionCompleted = true;
868868
return;

src/NHibernate/Transaction/AdoNetWithDistributedTransactionFactory.cs

Lines changed: 101 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections;
3+
using System.Threading;
34
using System.Transactions;
45
using NHibernate.Engine;
56
using NHibernate.Engine.Transaction;
@@ -26,57 +27,19 @@ public void EnlistInDistributedTransactionIfNeeded(ISessionImplementor session)
2627
{
2728
if (session.TransactionContext != null)
2829
return;
29-
30-
if (System.Transactions.Transaction.Current == null)
31-
return;
32-
33-
var transactionContext = new DistributedTransactionContext(session,
34-
System.Transactions.Transaction.Current);
35-
session.TransactionContext = transactionContext;
36-
logger.DebugFormat("enlisted into DTC transaction: {0}",
37-
transactionContext.AmbientTransation.IsolationLevel);
38-
session.AfterTransactionBegin(null);
39-
40-
TransactionCompletedEventHandler handler = null;
41-
42-
handler = delegate(object sender, TransactionEventArgs e)
43-
{
44-
using (new SessionIdLoggingContext(session.SessionId))
45-
{
46-
((DistributedTransactionContext) session.TransactionContext).IsInActiveTransaction = false;
47-
48-
bool wasSuccessful = false;
49-
try
50-
{
51-
wasSuccessful = e.Transaction.TransactionInformation.Status
52-
== TransactionStatus.Committed;
53-
}
54-
catch (ObjectDisposedException ode)
55-
{
56-
logger.Warn("Completed transaction was disposed, assuming transaction rollback", ode);
57-
}
58-
session.AfterTransactionCompletion(wasSuccessful, null);
59-
if (transactionContext.ShouldCloseSessionOnDistributedTransactionCompleted)
60-
{
61-
session.CloseSessionFromDistributedTransaction();
62-
}
63-
session.TransactionContext = null;
64-
}
6530

66-
e.Transaction.TransactionCompleted -= handler;
67-
};
68-
69-
transactionContext.AmbientTransation.TransactionCompleted += handler;
31+
var transaction = System.Transactions.Transaction.Current;
32+
if (transaction == null)
33+
return;
7034

71-
transactionContext.AmbientTransation.EnlistVolatile(transactionContext,
72-
EnlistmentOptions.EnlistDuringPrepareRequired);
35+
session.TransactionContext = new DistributedTransactionContext(session, transaction);
36+
logger.DebugFormat("enlisted into DTC transaction: {0}", transaction.IsolationLevel.ToString());
37+
session.AfterTransactionBegin(null);
7338
}
7439

7540
public bool IsInDistributedActiveTransaction(ISessionImplementor session)
7641
{
77-
var distributedTransactionContext = ((DistributedTransactionContext)session.TransactionContext);
78-
return distributedTransactionContext != null &&
79-
distributedTransactionContext.IsInActiveTransaction;
42+
return session.TransactionContext != null;
8043
}
8144

8245
public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, bool transacted)
@@ -92,35 +55,37 @@ public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork wo
9255

9356
public class DistributedTransactionContext : ITransactionContext, IEnlistmentNotification
9457
{
95-
public System.Transactions.Transaction AmbientTransation { get; set; }
58+
System.Transactions.Transaction _transaction;
59+
60+
ISessionImplementor _session;
61+
9662
public bool ShouldCloseSessionOnDistributedTransactionCompleted { get; set; }
97-
private readonly ISessionImplementor sessionImplementor;
98-
public bool IsInActiveTransaction;
9963

100-
public DistributedTransactionContext(ISessionImplementor sessionImplementor, System.Transactions.Transaction transaction)
64+
public DistributedTransactionContext(ISessionImplementor session, System.Transactions.Transaction transaction)
10165
{
102-
this.sessionImplementor = sessionImplementor;
103-
AmbientTransation = transaction.Clone();
104-
IsInActiveTransaction = true;
66+
_session = session;
67+
_transaction = transaction.Clone();
68+
_transaction.EnlistVolatile(this, EnlistmentOptions.EnlistDuringPrepareRequired);
10569
}
10670

107-
#region IEnlistmentNotification Members
108-
10971
void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
11072
{
111-
using (new SessionIdLoggingContext(sessionImplementor.SessionId))
73+
using (new SessionIdLoggingContext(_session.SessionId))
11274
{
11375
try
11476
{
115-
using (var tx = new TransactionScope(AmbientTransation))
77+
using (var tx = new TransactionScope(_transaction))
11678
{
117-
sessionImplementor.BeforeTransactionCompletion(null);
118-
if (sessionImplementor.FlushMode != FlushMode.Never && sessionImplementor.ConnectionManager.IsConnected)
79+
_session.BeforeTransactionCompletion(null);
80+
if (_session.FlushMode != FlushMode.Never && _session.ConnectionManager.IsConnected)
11981
{
120-
using (sessionImplementor.ConnectionManager.FlushingFromDtcTransaction)
82+
using (_session.ConnectionManager.FlushingFromDtcTransaction)
12183
{
122-
logger.Debug(string.Format("[session-id={0}] Flushing from Dtc Transaction", sessionImplementor.SessionId));
123-
sessionImplementor.Flush();
84+
logger.Debug(
85+
string.Format(
86+
"[session-id={0}] Flushing from Dtc Transaction",
87+
_session.SessionId.ToString()));
88+
_session.Flush();
12489
}
12590
}
12691
logger.Debug("prepared for DTC transaction");
@@ -131,53 +96,105 @@ void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
13196
}
13297
catch (Exception exception)
13398
{
134-
logger.Error("DTC transaction prepare phase failed", exception);
135-
preparingEnlistment.ForceRollback(exception);
99+
using (this)
100+
{
101+
try
102+
{
103+
logger.Error("DTC transaction prepare phase failed", exception);
104+
OnTransactionCompleted(false);
105+
}
106+
catch (Exception e)
107+
{
108+
logger.Warn("Issue preparing DTC transaction", e);
109+
}
110+
finally
111+
{
112+
preparingEnlistment.ForceRollback(exception);
113+
}
114+
}
136115
}
137116
}
138117
}
139118

140119
void IEnlistmentNotification.Commit(Enlistment enlistment)
141120
{
142-
using (new SessionIdLoggingContext(sessionImplementor.SessionId))
121+
using (new SessionIdLoggingContext(_session.SessionId))
122+
using (this)
143123
{
144-
logger.Debug("committing DTC transaction");
145-
// we have nothing to do here, since it is the actual
146-
// DB connection that will commit the transaction
147-
enlistment.Done();
148-
IsInActiveTransaction = false;
124+
try
125+
{
126+
logger.Debug("Committing DTC transaction");
127+
OnTransactionCompleted(true);
128+
}
129+
catch (Exception e)
130+
{
131+
logger.Warn("Exception happened at DTC transaction commit phase", e);
132+
}
133+
finally
134+
{
135+
enlistment.Done();
136+
}
149137
}
150138
}
151139

152140
void IEnlistmentNotification.Rollback(Enlistment enlistment)
153141
{
154-
using (new SessionIdLoggingContext(sessionImplementor.SessionId))
142+
using (new SessionIdLoggingContext(_session.SessionId))
143+
using (this)
155144
{
156-
logger.Debug("rolled back DTC transaction");
157-
// Currently AfterTransactionCompletion is called by the handler for the TransactionCompleted event.
158-
//sessionImplementor.AfterTransactionCompletion(false, null);
159-
enlistment.Done();
160-
IsInActiveTransaction = false;
145+
try
146+
{
147+
logger.Debug("Rolled back DTC transaction");
148+
OnTransactionCompleted(false);
149+
}
150+
catch (Exception e)
151+
{
152+
logger.Warn("Exception happened at DTC transaction rollback phase", e);
153+
}
154+
finally
155+
{
156+
enlistment.Done();
157+
}
161158
}
162159
}
163160

164161
void IEnlistmentNotification.InDoubt(Enlistment enlistment)
165162
{
166-
using (new SessionIdLoggingContext(sessionImplementor.SessionId))
163+
using (new SessionIdLoggingContext(_session.SessionId))
164+
using (this)
167165
{
168-
sessionImplementor.AfterTransactionCompletion(false, null);
169-
logger.Debug("DTC transaction is in doubt");
170-
enlistment.Done();
171-
IsInActiveTransaction = false;
166+
try
167+
{
168+
logger.Debug("DTC transaction is in doubt");
169+
OnTransactionCompleted(false);
170+
}
171+
catch (Exception e)
172+
{
173+
logger.Warn("Exception happened at DTC transaction in doubt phase", e);
174+
}
175+
finally
176+
{
177+
enlistment.Done();
178+
}
172179
}
173180
}
174181

175-
#endregion
176-
177182
public void Dispose()
178183
{
179-
if (AmbientTransation != null)
180-
AmbientTransation.Dispose();
184+
if (_transaction != null)
185+
_transaction.Dispose();
186+
_transaction = null;
187+
if (_session != null)
188+
_session.TransactionContext = null;
189+
_session = null;
190+
}
191+
192+
void OnTransactionCompleted(bool successful)
193+
{
194+
_session.TransactionContext = null;
195+
_session.AfterTransactionCompletion(successful, null);
196+
if (ShouldCloseSessionOnDistributedTransactionCompleted)
197+
_session.CloseSessionFromDistributedTransaction();
181198
}
182199
}
183200
}

0 commit comments

Comments
 (0)