Skip to content

Commit d52d74a

Browse files
NH-4018 - explicit setting for controlling enlistment in system transaction.
1 parent ec4c68a commit d52d74a

File tree

13 files changed

+114
-45
lines changed

13 files changed

+114
-45
lines changed

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,12 @@ public void When_commiting_items_in_DTC_transaction_will_add_items_to_2nd_level_
110110
// Will fail if transaction manager tries to enlist user supplied connection, due
111111
// to the transaction scope below.
112112

113-
using (var tx = new TransactionScope())
113+
using (var s = sessions.WithOptions().Connection(connection).OpenSession())
114114
{
115-
using (var s = sessions.WithOptions().Connection(connection).OpenSession())
116-
{
117-
Nums nums = null;
118-
Assert.DoesNotThrow(() => nums = s.Load<Nums>(29), "Failed loading entity from second level cache.");
119-
Assert.AreEqual(1, nums.NumA);
120-
Assert.AreEqual(3, nums.NumB);
121-
}
122-
tx.Complete();
115+
Nums nums = null;
116+
Assert.DoesNotThrow(() => nums = s.Load<Nums>(29), "Failed loading entity from second level cache.");
117+
Assert.AreEqual(1, nums.NumA);
118+
Assert.AreEqual(3, nums.NumB);
123119
}
124120
}
125121
finally

src/NHibernate.Test/SessionBuilder/Fixture.cs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ public class Fixture : TestCase
1212
protected override string MappingsAssembly => "NHibernate.Test";
1313

1414
protected override IList Mappings =>
15-
new string[]
16-
{
17-
"SessionBuilder.Mappings.hbm.xml"
18-
};
15+
new [] { "SessionBuilder.Mappings.hbm.xml" };
1916

2017
protected override void Configure(Configuration configuration)
2118
{
@@ -39,12 +36,34 @@ private void CanSetAutoClose<T>(T sb) where T : ISessionBuilder<T>
3936
var options = (ISessionCreationOptions)sb;
4037
CanSet(sb, sb.AutoClose, () => options.ShouldAutoClose,
4138
sb is ISharedSessionBuilder ssb ? ssb.AutoClose : default(Func<ISharedSessionBuilder>),
42-
// initial values
39+
// initial value
4340
false,
4441
// values
4542
true, false);
4643
}
4744

45+
[Test]
46+
public void CanSetAutoJoinTransaction()
47+
{
48+
var sb = sessions.WithOptions();
49+
CanSetAutoJoinTransaction(sb);
50+
using (var s = sb.OpenSession())
51+
{
52+
CanSetAutoJoinTransaction(s.SessionWithOptions());
53+
}
54+
}
55+
56+
private void CanSetAutoJoinTransaction<T>(T sb) where T : ISessionBuilder<T>
57+
{
58+
var options = (ISessionCreationOptions)sb;
59+
CanSet(sb, sb.AutoJoinTransaction, () => options.ShouldAutoJoinTransaction,
60+
sb is ISharedSessionBuilder ssb ? ssb.AutoJoinTransaction : default(Func<ISharedSessionBuilder>),
61+
// initial value
62+
true,
63+
// values
64+
false, true);
65+
}
66+
4867
[Test]
4968
public void CanSetConnection()
5069
{
@@ -140,7 +159,7 @@ private void CanSetConnectionReleaseMode<T>(T sb) where T : ISessionBuilder<T>
140159
var options = (ISessionCreationOptions)sb;
141160
CanSet(sb, sb.ConnectionReleaseMode, () => options.SessionConnectionReleaseMode,
142161
sb is ISharedSessionBuilder ssb ? ssb.ConnectionReleaseMode : default(Func<ISharedSessionBuilder>),
143-
// initial values
162+
// initial value
144163
sessions.Settings.ConnectionReleaseMode,
145164
// values
146165
ConnectionReleaseMode.OnClose, ConnectionReleaseMode.AfterStatement, ConnectionReleaseMode.AfterTransaction);
@@ -162,7 +181,7 @@ private void CanSetFlushMode<T>(T sb) where T : ISessionBuilder<T>
162181
var options = (ISessionCreationOptions)sb;
163182
CanSet(sb, sb.FlushMode, () => options.InitialSessionFlushMode,
164183
sb is ISharedSessionBuilder ssb ? ssb.FlushMode : default(Func<ISharedSessionBuilder>),
165-
// initial values
184+
// initial value
166185
sessions.Settings.DefaultFlushMode,
167186
// values
168187
FlushMode.Always, FlushMode.Auto, FlushMode.Commit, FlushMode.Manual);

src/NHibernate.Test/SystemTransactions/TransactionNotificationFixture.cs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System;
21
using System.Collections;
3-
using System.Data.Common;
4-
using System.Threading;
52
using System.Transactions;
63
using NUnit.Framework;
74

@@ -11,28 +8,37 @@ namespace NHibernate.Test.SystemTransactions
118
public class TransactionNotificationFixture : TestCase
129
{
1310
protected override IList Mappings
11+
=> new string[] { };
12+
13+
[Test]
14+
public void NoTransaction()
1415
{
15-
get { return new string[] {}; }
16+
var interceptor = new RecordingInterceptor();
17+
using (sessions.WithOptions().Interceptor(interceptor).OpenSession()) { }
18+
Assert.AreEqual(0, interceptor.afterTransactionBeginCalled);
19+
Assert.AreEqual(0, interceptor.beforeTransactionCompletionCalled);
20+
Assert.AreEqual(0, interceptor.afterTransactionCompletionCalled);
1621
}
1722

18-
1923
[Test]
20-
public void NoTransaction()
24+
public void TransactionDisabled()
2125
{
2226
var interceptor = new RecordingInterceptor();
23-
using (sessions.WithOptions().Interceptor(interceptor).OpenSession())
27+
using (var ts = new TransactionScope())
28+
using (sessions.WithOptions().Interceptor(interceptor).AutoJoinTransaction(false).OpenSession())
2429
{
25-
Assert.AreEqual(0, interceptor.afterTransactionBeginCalled);
26-
Assert.AreEqual(0, interceptor.beforeTransactionCompletionCalled);
27-
Assert.AreEqual(0, interceptor.afterTransactionCompletionCalled);
30+
ts.Complete();
2831
}
32+
Assert.AreEqual(0, interceptor.afterTransactionBeginCalled);
33+
Assert.AreEqual(0, interceptor.beforeTransactionCompletionCalled);
34+
Assert.AreEqual(0, interceptor.afterTransactionCompletionCalled);
2935
}
3036

3137
[Test]
3238
public void AfterBegin()
3339
{
3440
var interceptor = new RecordingInterceptor();
35-
using (new TransactionScope())
41+
using (new TransactionScope())
3642
using (sessions.WithOptions().Interceptor(interceptor).OpenSession())
3743
{
3844
Assert.AreEqual(1, interceptor.afterTransactionBeginCalled);
@@ -46,15 +52,15 @@ public void Complete()
4652
{
4753
var interceptor = new RecordingInterceptor();
4854
ISession session;
49-
using(var scope = new TransactionScope())
55+
using (var scope = new TransactionScope())
5056
{
5157
session = sessions.WithOptions().Interceptor(interceptor).OpenSession();
5258
scope.Complete();
5359
}
5460
session.Dispose();
5561
Assert.AreEqual(1, interceptor.beforeTransactionCompletionCalled);
5662
Assert.AreEqual(1, interceptor.afterTransactionCompletionCalled);
57-
63+
5864
}
5965

6066
[Test]

src/NHibernate/AdoNet/ConnectionManager.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ public ConnectionManager(
5151
ISessionImplementor session,
5252
DbConnection suppliedConnection,
5353
ConnectionReleaseMode connectionReleaseMode,
54-
IInterceptor interceptor)
54+
IInterceptor interceptor,
55+
bool shouldAutoJoinTransaction)
5556
{
5657
_session = session;
5758
_connection = suppliedConnection;
@@ -61,6 +62,7 @@ public ConnectionManager(
6162
_batcher = session.Factory.Settings.BatcherFactory.CreateBatcher(this, interceptor);
6263

6364
_ownConnection = suppliedConnection == null;
65+
ShouldAutoJoinTransaction = shouldAutoJoinTransaction;
6466
}
6567

6668
public bool IsInActiveExplicitTransaction
@@ -72,6 +74,8 @@ public bool IsInActiveTransaction
7274
public bool IsConnected
7375
=> _connection != null || _ownConnection;
7476

77+
public bool ShouldAutoJoinTransaction { get; }
78+
7579
public void Reconnect()
7680
{
7781
if (IsConnected)
@@ -120,6 +124,7 @@ private DbConnection DisconnectSuppliedConnection()
120124

121125
var c = _connection;
122126
_connection = null;
127+
_connectionAmbientTransaction = null;
123128
return c;
124129
}
125130

@@ -165,7 +170,8 @@ public DbConnection GetConnection()
165170
if (_ownConnection)
166171
{
167172
_connection = Factory.ConnectionProvider.GetConnection();
168-
_connectionAmbientTransaction = System.Transactions.Transaction.Current;
173+
174+
EnlistIfRequired(System.Transactions.Transaction.Current);
169175
if (Factory.Statistics.IsStatisticsEnabled)
170176
{
171177
Factory.StatisticsImplementor.Connect();
@@ -371,8 +377,7 @@ public DbCommand CreateCommand()
371377
public void EnlistIfRequired(System.Transactions.Transaction transaction)
372378
{
373379
if (_connection == null ||
374-
// Let user handles transaction if connection was supplied.
375-
!_ownConnection ||
380+
!ShouldAutoJoinTransaction ||
376381
// Most connections do not support enlisting in a system transaction while already participating
377382
// in a local transaction. They are not supposed to be mixed anyway.
378383
IsInActiveExplicitTransaction ||

src/NHibernate/Dialect/FirebirdDialect.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,8 @@ private static bool IsUnallowedDecimal(DbType dbType, int precision)
431431
/// Does this dialect support distributed transaction?
432432
/// </summary>
433433
/// <remarks>
434-
/// As of v2.5, fails rollback-ing changes when distributed: changes are instead persisted in database.
434+
/// As of v2.5 and 3.0.2, fails rollback-ing changes when distributed: changes are instead persisted in database.
435+
/// (With ADO .Net Provider 5.9.1)
435436
/// </remarks>
436437
public override bool SupportsDistributedTransactions => false;
437438

src/NHibernate/Engine/ISessionImplementor.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,20 +297,23 @@ public interface ISessionImplementor
297297

298298
IQuery GetNamedQuery(string queryName);
299299

300-
/// <summary> Determine whether the session is closed. Provided separately from
301-
/// {@link #isOpen()} as this method does not attempt any JTA sync
302-
/// registration, where as {@link #isOpen()} does; which makes this one
303-
/// nicer to use for most internal purposes.
300+
/// <summary>
301+
/// Determine whether the session is closed. Provided separately from
302+
/// <c>IsOpen</c> as this method does not attempt any system transaction sync
303+
/// registration, whereas <c>IsOpen</c> is allowed to (does not currently, but may do
304+
/// in a future version as it is the case in Hibernate); which makes this one
305+
/// nicer to use for most internal purposes.
304306
/// </summary>
305-
/// <returns> True if the session is closed; false otherwise.
307+
/// <returns>
308+
/// <see langword="true" /> if the session is closed; <see langword="false" /> otherwise.
306309
/// </returns>
307310
bool IsClosed { get; }
308311

309312
void Flush();
310313

311-
/// <summary>
312-
/// Does this <tt>Session</tt> have an active Hibernate transaction
313-
/// or is there a JTA transaction in progress?
314+
/// <summary>
315+
/// Does this <c>ISession</c> have an active NHibernate transaction
316+
/// or is there a system transaction in progress in which the session is enlisted?
314317
/// </summary>
315318
bool TransactionInProgress { get; }
316319

src/NHibernate/ISessionBuilder.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ public interface ISessionBuilder<T> where T : ISessionBuilder<T>
6565
/// <returns><see langword="this" />, for method chaining.</returns>
6666
T AutoClose(bool autoClose);
6767

68+
/// <summary>
69+
/// Should the session be automatically enlisted in ambient system transaction?
70+
/// Enabled by default. Disabling it does not prevent connections having auto-enlistment
71+
/// enabled to get enlisted in current ambient transaction when opened.
72+
/// </summary>
73+
/// <param name="autoJoinTransaction">Should the session be automatically explicitly
74+
/// enlisted in ambient transaction.</param>
75+
/// <returns><see langword="this" />, for method chaining.</returns>
76+
T AutoJoinTransaction(bool autoJoinTransaction);
77+
6878
/// <summary>
6979
/// Specify the initial FlushMode to use for the opened Session.
7080
/// </summary>

src/NHibernate/ISharedSessionBuilder.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ public interface ISharedSessionBuilder : ISessionBuilder<ISharedSessionBuilder>
99
{
1010
/// <summary>
1111
/// Signifies that the connection from the original session should be used to create the new session.
12+
/// Causes specified <c>ConnectionReleaseMode</c> and <c>AutoJoinTransaction</c> to be ignored and
13+
/// replaced by those of the original session.
1214
/// </summary>
1315
/// <returns><see langword="this" />, for method chaining.</returns>
1416
ISharedSessionBuilder Connection();
@@ -32,9 +34,15 @@ public interface ISharedSessionBuilder : ISessionBuilder<ISharedSessionBuilder>
3234
ISharedSessionBuilder FlushMode();
3335

3436
/// <summary>
35-
/// Signifies that the autoClose flag from the original session should be used to create the new session.
37+
/// Signifies that the AutoClose flag from the original session should be used to create the new session.
3638
/// </summary>
3739
/// <returns><see langword="this" />, for method chaining.</returns>
3840
ISharedSessionBuilder AutoClose();
41+
42+
/// <summary>
43+
/// Signifies that the AutoJoinTransaction flag from the original session should be used to create the new session.
44+
/// </summary>
45+
/// <returns><see langword="this" />, for method chaining.</returns>
46+
ISharedSessionBuilder AutoJoinTransaction();
3947
}
4048
}

src/NHibernate/Impl/ISessionCreationOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public interface ISessionCreationOptions
1414

1515
bool ShouldAutoClose { get; }
1616

17+
bool ShouldAutoJoinTransaction { get; }
18+
1719
DbConnection UserSuppliedConnection { get; }
1820

1921
IInterceptor SessionInterceptor { get; }

src/NHibernate/Impl/SessionFactoryImpl.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,7 @@ internal class SessionBuilderImpl<T> : ISessionBuilder<T>, ISessionCreationOptio
12901290
private ConnectionReleaseMode _connectionReleaseMode;
12911291
private FlushMode _flushMode;
12921292
private bool _autoClose;
1293+
private bool _autoJoinTransaction = true;
12931294

12941295
public SessionBuilderImpl(SessionFactoryImpl sessionFactory)
12951296
{
@@ -1314,6 +1315,8 @@ protected void SetSelf(T self)
13141315

13151316
public virtual bool ShouldAutoClose => _autoClose;
13161317

1318+
public virtual bool ShouldAutoJoinTransaction => _autoJoinTransaction;
1319+
13171320
public DbConnection UserSuppliedConnection => _connection;
13181321

13191322
// NH different implementation: Hibernate here ignore EmptyInterceptor.Instance too, resulting
@@ -1371,6 +1374,12 @@ public virtual T AutoClose(bool autoClose)
13711374
return _this;
13721375
}
13731376

1377+
public virtual T AutoJoinTransaction(bool autoJoinTransaction)
1378+
{
1379+
_autoJoinTransaction = autoJoinTransaction;
1380+
return _this;
1381+
}
1382+
13741383
public virtual T FlushMode(FlushMode flushMode)
13751384
{
13761385
_flushMode = flushMode;
@@ -1402,6 +1411,8 @@ public IStatelessSessionBuilder Connection(DbConnection connection)
14021411

14031412
public bool ShouldAutoClose => false;
14041413

1414+
public bool ShouldAutoJoinTransaction => false;
1415+
14051416
public DbConnection UserSuppliedConnection => _connection;
14061417

14071418
public IInterceptor SessionInterceptor => EmptyInterceptor.Instance;

src/NHibernate/Impl/SessionImpl.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ internal SessionImpl(SessionFactoryImpl factory, ISessionCreationOptions options
204204
}
205205
else
206206
{
207-
connectionManager = new ConnectionManager(this, options.UserSuppliedConnection, connectionReleaseMode, Interceptor);
207+
connectionManager = new ConnectionManager(
208+
this, options.UserSuppliedConnection, connectionReleaseMode, Interceptor, options.ShouldAutoJoinTransaction);
208209
}
209210

210211
if (factory.Statistics.IsStatisticsEnabled)
@@ -2545,6 +2546,8 @@ public virtual ISharedSessionBuilder Connection()
25452546

25462547
public virtual ISharedSessionBuilder AutoClose() => AutoClose(_session.autoCloseSessionEnabled);
25472548

2549+
public virtual ISharedSessionBuilder AutoJoinTransaction() => AutoJoinTransaction(_session.ConnectionManager.ShouldAutoJoinTransaction);
2550+
25482551
// NH different implementation, avoid an error case.
25492552
public override ISharedSessionBuilder Connection(DbConnection connection)
25502553
{

src/NHibernate/Impl/StatelessSessionImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ internal StatelessSessionImpl(SessionFactoryImpl factory, ISessionCreationOption
3939
{
4040
temporaryPersistenceContext = new StatefulPersistenceContext(this);
4141
connectionManager = new ConnectionManager(this, options.UserSuppliedConnection, ConnectionReleaseMode.AfterTransaction,
42-
EmptyInterceptor.Instance);
42+
EmptyInterceptor.Instance, options.ShouldAutoJoinTransaction);
4343

4444
if (log.IsDebugEnabled)
4545
{

src/NHibernate/Transaction/AdoNetWithDistributedTransactionFactory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public ITransaction CreateTransaction(ISessionImplementor session)
2525

2626
public void EnlistInDistributedTransactionIfNeeded(ISessionImplementor session)
2727
{
28+
if (!session.ConnectionManager.ShouldAutoJoinTransaction)
29+
{
30+
return;
31+
}
32+
2833
// Ensure the session does not run on a thread supposed to be blocked, waiting
2934
// for transaction completion.
3035
session.TransactionContext?.WaitOne();

0 commit comments

Comments
 (0)