Skip to content

Commit 6ae4a1b

Browse files
NH-4011 - Allowing easier customization of transaction factories for users.
* More xml documentation. * More virtual members. * More pre-conditions.
1 parent fa5b0a2 commit 6ae4a1b

File tree

7 files changed

+427
-153
lines changed

7 files changed

+427
-153
lines changed

src/NHibernate/Async/Transaction/AdoNetTransactionFactory.cs

Lines changed: 86 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -28,88 +28,111 @@ namespace NHibernate.Transaction
2828
public partial class AdoNetTransactionFactory : ITransactionFactory
2929
{
3030

31-
public async Task ExecuteWorkInIsolationAsync(ISessionImplementor session, IIsolatedWork work, bool transacted, CancellationToken cancellationToken)
31+
/// <inheritdoc />
32+
public virtual Task ExecuteWorkInIsolationAsync(ISessionImplementor session, IIsolatedWork work, bool transacted, CancellationToken cancellationToken)
3233
{
33-
cancellationToken.ThrowIfCancellationRequested();
34-
DbConnection connection = null;
35-
DbTransaction trans = null;
36-
// bool wasAutoCommit = false;
37-
try
34+
if (session == null)
35+
throw new ArgumentNullException(nameof(session));
36+
if (work == null)
37+
throw new ArgumentNullException(nameof(work));
38+
if (cancellationToken.IsCancellationRequested)
39+
{
40+
return Task.FromCanceled<object>(cancellationToken);
41+
}
42+
return InternalExecuteWorkInIsolationAsync();
43+
async Task InternalExecuteWorkInIsolationAsync()
3844
{
39-
// We make an exception for SQLite and use the session's connection,
40-
// since SQLite only allows one connection to the database.
41-
if (session.Factory.Dialect is SQLiteDialect)
42-
connection = session.Connection;
43-
else
44-
connection = await (session.Factory.ConnectionProvider.GetConnectionAsync(cancellationToken)).ConfigureAwait(false);
4545

46-
if (transacted)
46+
DbConnection connection = null;
47+
DbTransaction trans = null;
48+
// bool wasAutoCommit = false;
49+
try
4750
{
48-
trans = connection.BeginTransaction();
49-
// TODO NH: a way to read the autocommit state is needed
50-
//if (TransactionManager.GetAutoCommit(connection))
51-
//{
52-
// wasAutoCommit = true;
53-
// TransactionManager.SetAutoCommit(connection, false);
54-
//}
55-
}
51+
// We make an exception for SQLite and use the session's connection,
52+
// since SQLite only allows one connection to the database.
53+
if (session.Factory.Dialect is SQLiteDialect)
54+
connection = session.Connection;
55+
else
56+
connection = await (session.Factory.ConnectionProvider.GetConnectionAsync(cancellationToken)).ConfigureAwait(false);
5657

57-
await (work.DoWorkAsync(connection, trans, cancellationToken)).ConfigureAwait(false);
58+
if (transacted)
59+
{
60+
trans = connection.BeginTransaction();
61+
// TODO NH: a way to read the autocommit state is needed
62+
//if (TransactionManager.GetAutoCommit(connection))
63+
//{
64+
// wasAutoCommit = true;
65+
// TransactionManager.SetAutoCommit(connection, false);
66+
//}
67+
}
5868

59-
if (transacted)
60-
{
61-
trans.Commit();
62-
//TransactionManager.Commit(connection);
69+
await (work.DoWorkAsync(connection, trans, cancellationToken)).ConfigureAwait(false);
70+
71+
if (transacted)
72+
{
73+
trans.Commit();
74+
//TransactionManager.Commit(connection);
75+
}
6376
}
64-
}
65-
catch (Exception t)
66-
{
67-
using (new SessionIdLoggingContext(session.SessionId))
77+
catch (Exception t)
6878
{
69-
try
79+
using (new SessionIdLoggingContext(session.SessionId))
7080
{
71-
if (trans != null && connection.State != ConnectionState.Closed)
81+
try
7282
{
73-
trans.Rollback();
83+
if (trans != null && connection.State != ConnectionState.Closed)
84+
{
85+
trans.Rollback();
86+
}
87+
}
88+
catch (Exception ignore)
89+
{
90+
isolaterLog.Debug("Unable to rollback transaction", ignore);
7491
}
75-
}
76-
catch (Exception ignore)
77-
{
78-
isolaterLog.Debug("unable to release connection on exception [" + ignore + "]");
79-
}
8092

81-
if (t is HibernateException)
82-
{
83-
throw;
93+
if (t is HibernateException)
94+
{
95+
throw;
96+
}
97+
else if (t is DbException)
98+
{
99+
throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, t,
100+
"error performing isolated work");
101+
}
102+
else
103+
{
104+
throw new HibernateException("error performing isolated work", t);
105+
}
84106
}
85-
else if (t is DbException)
107+
}
108+
finally
109+
{
110+
//if (transacted && wasAutoCommit)
111+
//{
112+
// try
113+
// {
114+
// // TODO NH: reset autocommit
115+
// // TransactionManager.SetAutoCommit(connection, true);
116+
// }
117+
// catch (Exception)
118+
// {
119+
// log.Debug("was unable to reset connection back to auto-commit");
120+
// }
121+
//}
122+
123+
try
86124
{
87-
throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, t,
88-
"error performing isolated work");
125+
trans?.Dispose();
89126
}
90-
else
127+
catch (Exception ignore)
91128
{
92-
throw new HibernateException("error performing isolated work", t);
129+
isolaterLog.Warn("Unable to dispose transaction", ignore);
93130
}
131+
132+
if (session.Factory.Dialect is SQLiteDialect == false)
133+
session.Factory.ConnectionProvider.CloseConnection(connection);
94134
}
95135
}
96-
finally
97-
{
98-
//if (transacted && wasAutoCommit)
99-
//{
100-
// try
101-
// {
102-
// // TODO NH: reset autocommit
103-
// // TransactionManager.SetAutoCommit(connection, true);
104-
// }
105-
// catch (Exception)
106-
// {
107-
// log.Debug("was unable to reset connection back to auto-commit");
108-
// }
109-
//}
110-
if (session.Factory.Dialect is SQLiteDialect == false)
111-
session.Factory.ConnectionProvider.CloseConnection(connection);
112-
}
113136
}
114137
}
115138
}

src/NHibernate/Async/Transaction/AdoNetWithSystemTransactionFactory.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System.Linq;
1414
using System.Threading;
1515
using System.Transactions;
16+
using NHibernate.AdoNet;
1617
using NHibernate.Engine;
1718
using NHibernate.Engine.Transaction;
1819
using NHibernate.Impl;
@@ -24,17 +25,16 @@ namespace NHibernate.Transaction
2425
/// <content>
2526
/// Contains generated async methods
2627
/// </content>
27-
public partial class AdoNetWithSystemTransactionFactory : ITransactionFactory
28+
public partial class AdoNetWithSystemTransactionFactory : AdoNetTransactionFactory
2829
{
2930

30-
public async Task ExecuteWorkInIsolationAsync(ISessionImplementor session, IIsolatedWork work, bool transacted, CancellationToken cancellationToken)
31+
/// <inheritdoc />
32+
public override async Task ExecuteWorkInIsolationAsync(ISessionImplementor session, IIsolatedWork work, bool transacted, CancellationToken cancellationToken)
3133
{
3234
cancellationToken.ThrowIfCancellationRequested();
3335
using (var tx = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
3436
{
35-
// instead of duplicating the logic, we suppress the system transaction and create
36-
// our own transaction instead
37-
await (_adoNetTransactionFactory.ExecuteWorkInIsolationAsync(session, work, transacted, cancellationToken)).ConfigureAwait(false);
37+
await (base.ExecuteWorkInIsolationAsync(session, work, transacted, cancellationToken)).ConfigureAwait(false);
3838
tx.Complete();
3939
}
4040
}

src/NHibernate/Async/Transaction/ITransactionFactory.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ namespace NHibernate.Transaction
2323
public partial interface ITransactionFactory
2424
{
2525

26+
/// <summary>
27+
/// Execute a work outside of the current transaction (if any).
28+
/// </summary>
29+
/// <param name="session">The session for which an isolated work has to be executed.</param>
30+
/// <param name="work">The work to execute.</param>
31+
/// <param name="transacted"><see langword="true" /> for encapsulating the work in a dedicated
32+
/// transaction, <see langword="false" /> for not transacting it.</param>
33+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
2634
Task ExecuteWorkInIsolationAsync(ISessionImplementor session, IIsolatedWork work, bool transacted, CancellationToken cancellationToken);
2735
}
2836
}

src/NHibernate/Transaction/AdoNetTransactionFactory.cs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,22 @@
1010

1111
namespace NHibernate.Transaction
1212
{
13+
/// <summary>
14+
/// Minimal <see cref="ITransaction"/> factory implementation.
15+
/// Does not support system <see cref="System.Transactions.Transaction"/>.
16+
/// </summary>
1317
public partial class AdoNetTransactionFactory : ITransactionFactory
1418
{
1519
private readonly IInternalLogger isolaterLog = LoggerProvider.LoggerFor(typeof(ITransactionFactory));
1620

17-
public ITransaction CreateTransaction(ISessionImplementor session)
21+
/// <inheritdoc />
22+
public virtual ITransaction CreateTransaction(ISessionImplementor session)
1823
{
1924
return new AdoTransaction(session);
2025
}
2126

22-
public void EnlistInSystemTransactionIfNeeded(ISessionImplementor session)
27+
/// <inheritdoc />
28+
public virtual void EnlistInSystemTransactionIfNeeded(ISessionImplementor session)
2329
{
2430
// nothing need to do here, we only support local transactions with this factory
2531
}
@@ -30,13 +36,20 @@ public virtual void ExplicitJoinSystemTransaction(ISessionImplementor session)
3036
throw new NotSupportedException("The current transaction factory does not support system transactions.");
3137
}
3238

33-
public bool IsInActiveSystemTransaction(ISessionImplementor session)
39+
/// <inheritdoc />
40+
public virtual bool IsInActiveSystemTransaction(ISessionImplementor session)
3441
{
3542
return false;
3643
}
3744

38-
public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, bool transacted)
45+
/// <inheritdoc />
46+
public virtual void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, bool transacted)
3947
{
48+
if (session == null)
49+
throw new ArgumentNullException(nameof(session));
50+
if (work == null)
51+
throw new ArgumentNullException(nameof(work));
52+
4053
DbConnection connection = null;
4154
DbTransaction trans = null;
4255
// bool wasAutoCommit = false;
@@ -81,7 +94,7 @@ public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork wo
8194
}
8295
catch (Exception ignore)
8396
{
84-
isolaterLog.Debug("unable to release connection on exception [" + ignore + "]");
97+
isolaterLog.Debug("Unable to rollback transaction", ignore);
8598
}
8699

87100
if (t is HibernateException)
@@ -91,7 +104,7 @@ public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork wo
91104
else if (t is DbException)
92105
{
93106
throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, t,
94-
"error performing isolated work");
107+
"error performing isolated work");
95108
}
96109
else
97110
{
@@ -113,12 +126,23 @@ public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork wo
113126
// log.Debug("was unable to reset connection back to auto-commit");
114127
// }
115128
//}
129+
130+
try
131+
{
132+
trans?.Dispose();
133+
}
134+
catch (Exception ignore)
135+
{
136+
isolaterLog.Warn("Unable to dispose transaction", ignore);
137+
}
138+
116139
if (session.Factory.Dialect is SQLiteDialect == false)
117140
session.Factory.ConnectionProvider.CloseConnection(connection);
118141
}
119142
}
120143

121-
public void Configure(IDictionary<string, string> props)
144+
/// <inheritdoc />
145+
public virtual void Configure(IDictionary<string, string> props)
122146
{
123147
}
124148
}

0 commit comments

Comments
 (0)