diff --git a/src/NHibernate.Test/DebugSessionFactory.cs b/src/NHibernate.Test/DebugSessionFactory.cs index dfb6e5ce571..801b8f3a76e 100644 --- a/src/NHibernate.Test/DebugSessionFactory.cs +++ b/src/NHibernate.Test/DebugSessionFactory.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Data.Common; +using System.Linq; using System.Threading; using log4net; using NHibernate.Cache; @@ -53,29 +54,41 @@ public bool CheckSessionsWereClosed() var allClosed = true; foreach (var session in _openedSessions) { - if (session.IsOpen) + // Do not inverse, we want to close all of them. + allClosed = CheckSessionWasClosed(session) && allClosed; + // Catches only session opened from another one while sharing the connection. Those + // opened without sharing the connection stay un-monitored. + foreach (var dependentSession in session.ConnectionManager.DependentSessions.ToList()) { - if (session.TransactionContext?.ShouldCloseSessionOnDistributedTransactionCompleted ?? false) - { - // Delayed transactions not having completed and closed their sessions? Give them a chance to complete. - Thread.Sleep(100); - if (!session.IsOpen) - { - _log.Warn($"Test case had a delayed close of session {session.SessionId}."); - continue; - } - } - - _log.Error($"Test case didn't close session {session.SessionId}, closing"); - allClosed = false; - (session as ISession)?.Close(); - (session as IStatelessSession)?.Close(); + allClosed = CheckSessionWasClosed(dependentSession) && allClosed; } } return allClosed; } + private bool CheckSessionWasClosed(ISessionImplementor session) + { + if (!session.IsOpen) + return true; + + if (session.TransactionContext?.ShouldCloseSessionOnDistributedTransactionCompleted ?? false) + { + // Delayed transactions not having completed and closed their sessions? Give them a chance to complete. + Thread.Sleep(100); + if (!session.IsOpen) + { + _log.Warn($"Test case had a delayed close of session {session.SessionId}."); + return true; + } + } + + _log.Error($"Test case didn't close session {session.SessionId}, closing"); + (session as ISession)?.Close(); + (session as IStatelessSession)?.Close(); + return false; + } + ISessionBuilder ISessionFactory.WithOptions() { return new SessionBuilder(ActualFactory.WithOptions(), this); diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index d8c04e36a8a..198851b6e86 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -1464,6 +1464,8 @@ + + @@ -3286,6 +3288,7 @@ + diff --git a/src/NHibernate.Test/SystemTransactions/TransactionFixture.cs b/src/NHibernate.Test/SystemTransactions/TransactionFixture.cs index cad02f86e90..de5dd5eb7c6 100644 --- a/src/NHibernate.Test/SystemTransactions/TransactionFixture.cs +++ b/src/NHibernate.Test/SystemTransactions/TransactionFixture.cs @@ -1,26 +1,23 @@ -using System.Collections; +using System.Collections.Generic; +using System.Linq; using System.Transactions; -using NHibernate.DomainModel; +using NHibernate.Linq; +using NHibernate.Test.TransactionTest; using NUnit.Framework; namespace NHibernate.Test.SystemTransactions { [TestFixture] - public class TransactionFixture : TestCase + public class TransactionFixture : TransactionFixtureBase { - protected override IList Mappings - { - get { return new string[] { "WZ.hbm.xml" }; } - } - [Test] public void CanUseSystemTransactionsToCommit() { - object identifier; + int identifier; using(ISession session = Sfi.OpenSession()) using(TransactionScope tx = new TransactionScope()) { - W s = new W(); + var s = new Person(); session.Save(s); identifier = s.Id; tx.Complete(); @@ -29,11 +26,84 @@ public void CanUseSystemTransactionsToCommit() using (ISession session = Sfi.OpenSession()) using (TransactionScope tx = new TransactionScope()) { - W w = session.Get(identifier); + var w = session.Get(identifier); Assert.IsNotNull(w); session.Delete(w); tx.Complete(); } } + + [Test] + public void FlushFromTransactionAppliesToDisposedSharingSession() + { + var flushOrder = new List(); + using (var s = OpenSession(new TestInterceptor(0, flushOrder))) + { + var builder = s.SessionWithOptions().Connection(); + + using (var t = new TransactionScope()) + { + var p1 = new Person(); + var p2 = new Person(); + var p3 = new Person(); + var p4 = new Person(); + + using (var s1 = builder.Interceptor(new TestInterceptor(1, flushOrder)).OpenSession()) + s1.Save(p1); + using (var s2 = builder.Interceptor(new TestInterceptor(2, flushOrder)).OpenSession()) + { + s2.Save(p2); + using (var s3 = s2.SessionWithOptions().Connection().Interceptor(new TestInterceptor(3, flushOrder)).OpenSession()) + s3.Save(p3); + } + s.Save(p4); + t.Complete(); + } + } + + Assert.That(flushOrder, Is.EqualTo(new[] { 0, 1, 2, 3 })); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + Assert.That(s.Query().Count(), Is.EqualTo(4)); + t.Commit(); + } + } + + [Test] + public void FlushFromTransactionAppliesToSharingSession() + { + var flushOrder = new List(); + using (var s = OpenSession(new TestInterceptor(0, flushOrder))) + { + var builder = s.SessionWithOptions().Connection(); + + using (var s1 = builder.Interceptor(new TestInterceptor(1, flushOrder)).OpenSession()) + using (var s2 = builder.Interceptor(new TestInterceptor(2, flushOrder)).OpenSession()) + using (var s3 = s2.SessionWithOptions().Connection().Interceptor(new TestInterceptor(3, flushOrder)).OpenSession()) + using (var t = new TransactionScope()) + { + var p1 = new Person(); + var p2 = new Person(); + var p3 = new Person(); + var p4 = new Person(); + s1.Save(p1); + s2.Save(p2); + s3.Save(p3); + s.Save(p4); + t.Complete(); + } + } + + Assert.That(flushOrder, Is.EqualTo(new[] { 0, 1, 2, 3 })); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + Assert.That(s.Query().Count(), Is.EqualTo(4)); + t.Commit(); + } + } } } \ No newline at end of file diff --git a/src/NHibernate.Test/TransactionTest/Person.cs b/src/NHibernate.Test/TransactionTest/Person.cs new file mode 100644 index 00000000000..39ad3f19626 --- /dev/null +++ b/src/NHibernate.Test/TransactionTest/Person.cs @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.TransactionTest +{ + public class Person + { + public virtual int Id { get; set; } + + public virtual DateTime CreatedAt { get; set; } = DateTime.Now; + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/TransactionTest/Person.hbm.xml b/src/NHibernate.Test/TransactionTest/Person.hbm.xml new file mode 100644 index 00000000000..49c9ffc20e1 --- /dev/null +++ b/src/NHibernate.Test/TransactionTest/Person.hbm.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/src/NHibernate.Test/TransactionTest/TransactionFixture.cs b/src/NHibernate.Test/TransactionTest/TransactionFixture.cs index 52b7a619c11..3c3d2e54a09 100644 --- a/src/NHibernate.Test/TransactionTest/TransactionFixture.cs +++ b/src/NHibernate.Test/TransactionTest/TransactionFixture.cs @@ -1,19 +1,15 @@ using System; using System.Collections; -using System.Data.Common; +using System.Collections.Generic; +using System.Linq; +using NHibernate.Linq; using NUnit.Framework; namespace NHibernate.Test.TransactionTest { [TestFixture] - public class TransactionFixture : TestCase + public class TransactionFixture : TransactionFixtureBase { - protected override IList Mappings - { - // The mapping is only actually needed in one test - get { return new string[] {"Simple.hbm.xml"}; } - } - [Test] public void SecondTransactionShouldntBeCommitted() { @@ -74,25 +70,25 @@ public void CommandAfterTransactionShouldWork() { using (ISession s = OpenSession()) { - using (ITransaction t = s.BeginTransaction()) + using (s.BeginTransaction()) { } - s.CreateQuery("from Simple").List(); + s.CreateQuery("from Person").List(); using (ITransaction t = s.BeginTransaction()) { t.Commit(); } - s.CreateQuery("from Simple").List(); + s.CreateQuery("from Person").List(); using (ITransaction t = s.BeginTransaction()) { t.Rollback(); } - s.CreateQuery("from Simple").List(); + s.CreateQuery("from Person").List(); } } @@ -141,5 +137,40 @@ public void WasCommittedOrRolledBack() } } } + + [Test] + public void FlushFromTransactionAppliesToSharingSession() + { + var flushOrder = new List(); + using (var s = OpenSession(new TestInterceptor(0, flushOrder))) + { + var builder = s.SessionWithOptions().Connection(); + + using (var s1 = builder.Interceptor(new TestInterceptor(1, flushOrder)).OpenSession()) + using (var s2 = builder.Interceptor(new TestInterceptor(2, flushOrder)).OpenSession()) + using (var s3 = s1.SessionWithOptions().Connection().Interceptor(new TestInterceptor(3, flushOrder)).OpenSession()) + using (var t = s.BeginTransaction()) + { + var p1 = new Person(); + var p2 = new Person(); + var p3 = new Person(); + var p4 = new Person(); + s1.Save(p1); + s2.Save(p2); + s3.Save(p3); + s.Save(p4); + t.Commit(); + } + } + + Assert.That(flushOrder, Is.EqualTo(new[] { 0, 1, 2, 3 })); + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + Assert.That(s.Query().Count(), Is.EqualTo(4)); + t.Commit(); + } + } } } \ No newline at end of file diff --git a/src/NHibernate.Test/TransactionTest/TransactionFixtureBase.cs b/src/NHibernate.Test/TransactionTest/TransactionFixtureBase.cs new file mode 100644 index 00000000000..2ad374951cd --- /dev/null +++ b/src/NHibernate.Test/TransactionTest/TransactionFixtureBase.cs @@ -0,0 +1,39 @@ +using System.Collections; +using System.Collections.Generic; + +namespace NHibernate.Test.TransactionTest +{ + public abstract class TransactionFixtureBase : TestCase + { + protected override IList Mappings => new[] { "TransactionTest.Person.hbm.xml" }; + + protected override string MappingsAssembly => "NHibernate.Test"; + + protected override void OnTearDown() + { + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Delete("from System.Object"); + t.Commit(); + } + } + + public class TestInterceptor : EmptyInterceptor + { + private readonly int _numero; + private readonly List _flushOrder; + + public TestInterceptor(int numero, List flushOrder) + { + _numero = numero; + _flushOrder = flushOrder; + } + + public override void PreFlush(ICollection entitites) + { + _flushOrder.Add(_numero); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate/AdoNet/ConnectionManager.cs b/src/NHibernate/AdoNet/ConnectionManager.cs index 78274014e0f..258fc4aa7fe 100644 --- a/src/NHibernate/AdoNet/ConnectionManager.cs +++ b/src/NHibernate/AdoNet/ConnectionManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Runtime.Serialization; @@ -41,6 +42,17 @@ public interface Callback private readonly ISessionImplementor session; private readonly ConnectionReleaseMode connectionReleaseMode; private readonly IInterceptor interceptor; + private readonly List _dependentSessions = new List(); + + /// + /// The session responsible for the lifecycle of the connection manager. + /// + public ISessionImplementor Session => session; + + /// + /// The sessions using the connection manager of the session responsible for it. + /// + public IReadOnlyCollection DependentSessions => _dependentSessions; [NonSerialized] private bool _releasesEnabled = true; @@ -63,6 +75,16 @@ public ConnectionManager( ownConnection = suppliedConnection == null; } + public void AddDependentSession(ISessionImplementor session) + { + _dependentSessions.Add(session); + } + + public void RemoveDependentSession(ISessionImplementor session) + { + _dependentSessions.Remove(session); + } + public bool IsInActiveTransaction { get diff --git a/src/NHibernate/Engine/ISessionImplementor.cs b/src/NHibernate/Engine/ISessionImplementor.cs index e93c9082035..c59be757f01 100644 --- a/src/NHibernate/Engine/ISessionImplementor.cs +++ b/src/NHibernate/Engine/ISessionImplementor.cs @@ -186,6 +186,11 @@ public interface ISessionImplementor /// void BeforeTransactionCompletion(ITransaction tx); + /// + /// + /// + void FlushBeforeTransactionCompletion(); + /// /// Notify the session that the transaction completed, so we no longer own the old locks. /// (Also we should release cache softlocks). May be called multiple times during the transaction diff --git a/src/NHibernate/ISharedSessionBuilder.cs b/src/NHibernate/ISharedSessionBuilder.cs index 9bbbddbc6cd..964cf9464a7 100644 --- a/src/NHibernate/ISharedSessionBuilder.cs +++ b/src/NHibernate/ISharedSessionBuilder.cs @@ -9,6 +9,8 @@ public interface ISharedSessionBuilder : ISessionBuilder { /// /// Signifies that the connection from the original session should be used to create the new session. + /// The original session remains responsible for it and its closing will cause sharing sessions to be no + /// more usable. /// /// , for method chaining. ISharedSessionBuilder Connection(); diff --git a/src/NHibernate/Impl/AbstractSessionImpl.cs b/src/NHibernate/Impl/AbstractSessionImpl.cs index 9c6e844fa62..cc1363f461c 100644 --- a/src/NHibernate/Impl/AbstractSessionImpl.cs +++ b/src/NHibernate/Impl/AbstractSessionImpl.cs @@ -152,6 +152,7 @@ public virtual IList List(CriteriaImpl criteria) public abstract IEntityPersister GetEntityPersister(string entityName, object obj); public abstract void AfterTransactionBegin(ITransaction tx); public abstract void BeforeTransactionCompletion(ITransaction tx); + public abstract void FlushBeforeTransactionCompletion(); public abstract void AfterTransactionCompletion(bool successful, ITransaction tx); public abstract object GetContextEntityIdentifier(object obj); public abstract object Instantiate(string clazz, object id); @@ -430,6 +431,7 @@ protected void AfterOperation(bool success) if (!ConnectionManager.IsInActiveTransaction) { ConnectionManager.AfterNonTransactionalQuery(success); + ConnectionManager.AfterTransaction(); AfterTransactionCompletion(success, null); } } diff --git a/src/NHibernate/Impl/SessionImpl.cs b/src/NHibernate/Impl/SessionImpl.cs index bc0704ceebc..a6e86c5d984 100644 --- a/src/NHibernate/Impl/SessionImpl.cs +++ b/src/NHibernate/Impl/SessionImpl.cs @@ -201,6 +201,7 @@ internal SessionImpl(SessionFactoryImpl factory, ISessionCreationOptions options if (options.UserSuppliedConnection != null) throw new SessionException("Cannot simultaneously share transaction context and specify connection"); connectionManager = sharedOptions.ConnectionManager; + connectionManager.AddDependentSession(this); } else { @@ -298,8 +299,8 @@ public DbConnection Close() { if (!_transactionCoordinatorShared) return connectionManager.Close(); - else - return null; + connectionManager.RemoveDependentSession(this); + return null; } finally { @@ -319,26 +320,23 @@ public override void AfterTransactionCompletion(bool success, ITransaction tx) using (new SessionIdLoggingContext(SessionId)) { log.Debug("transaction completion"); + + persistenceContext.AfterTransactionCompletion(); + actionQueue.AfterTransactionCompletion(success); + if (Factory.Statistics.IsStatisticsEnabled) { Factory.StatisticsImplementor.EndTransaction(success); } - connectionManager.AfterTransaction(); - persistenceContext.AfterTransactionCompletion(); - actionQueue.AfterTransactionCompletion(success); - if (!_transactionCoordinatorShared) + try { - try - { - Interceptor.AfterTransactionCompletion(tx); - } - catch (Exception t) - { - log.Error("exception in interceptor afterTransactionCompletion()", t); - } + Interceptor.AfterTransactionCompletion(tx); + } + catch (Exception t) + { + log.Error("exception in interceptor afterTransactionCompletion()", t); } - //if (autoClear) // Clear(); @@ -2101,23 +2099,30 @@ public override void BeforeTransactionCompletion(ITransaction tx) using (new SessionIdLoggingContext(SessionId)) { log.Debug("before transaction completion"); + FlushBeforeTransactionCompletion(); actionQueue.BeforeTransactionCompletion(); - if (!_transactionCoordinatorShared) + try { - try - { - Interceptor.BeforeTransactionCompletion(tx); - } - catch (Exception e) - { - log.Error("exception in interceptor BeforeTransactionCompletion()", e); + Interceptor.BeforeTransactionCompletion(tx); + } + catch (Exception e) + { + log.Error("exception in interceptor BeforeTransactionCompletion()", e); - throw; - } + throw; } } } + public override void FlushBeforeTransactionCompletion() + { + using (new SessionIdLoggingContext(SessionId)) + { + if (FlushMode != FlushMode.Manual) + Flush(); + } + } + public ISession SetBatchSize(int batchSize) { Batcher.BatchSize = batchSize; diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 6933c4e3a82..c0f5565ab74 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -27,8 +27,10 @@ namespace NHibernate.Impl public class StatelessSessionImpl : AbstractSessionImpl, IStatelessSession { private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(StatelessSessionImpl)); + [NonSerialized] private readonly ConnectionManager connectionManager; + [NonSerialized] private readonly StatefulPersistenceContext temporaryPersistenceContext; @@ -179,7 +181,7 @@ public override void List(CriteriaImpl criteria, IList results) temporaryPersistenceContext.Clear(); } } - + public override IEnumerable Enumerable(IQueryExpression queryExpression, QueryParameters queryParameters) { throw new NotImplementedException(); @@ -216,16 +218,22 @@ public override void AfterTransactionBegin(ITransaction tx) public override void BeforeTransactionCompletion(ITransaction tx) { + FlushBeforeTransactionCompletion(); } - public override void AfterTransactionCompletion(bool successful, ITransaction tx) + public override void FlushBeforeTransactionCompletion() { using (new SessionIdLoggingContext(SessionId)) { - connectionManager.AfterTransaction(); + if (FlushMode != FlushMode.Manual) + Flush(); } } + public override void AfterTransactionCompletion(bool successful, ITransaction tx) + { + } + public override object GetContextEntityIdentifier(object obj) { CheckAndUpdateSessionStatus(); @@ -838,6 +846,7 @@ public ITransaction BeginTransaction(IsolationLevel isolationLevel) #endregion #region IDisposable Members + private bool _isAlreadyDisposed; /// diff --git a/src/NHibernate/Transaction/AdoNetWithDistributedTransactionFactory.cs b/src/NHibernate/Transaction/AdoNetWithDistributedTransactionFactory.cs index d2cdceb3c2c..6b7fc7ab64b 100644 --- a/src/NHibernate/Transaction/AdoNetWithDistributedTransactionFactory.cs +++ b/src/NHibernate/Transaction/AdoNetWithDistributedTransactionFactory.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Linq; using System.Transactions; using NHibernate.Engine; using NHibernate.Engine.Transaction; @@ -29,13 +30,26 @@ public void EnlistInDistributedTransactionIfNeeded(ISessionImplementor session) if (System.Transactions.Transaction.Current == null) return; - + + var originatingSession = session.ConnectionManager.Session; + if (originatingSession != session) + { + session.TransactionContext = new DependentContext(); + } + + if (originatingSession.TransactionContext != null) + return; + + session = originatingSession; + var transactionContext = new DistributedTransactionContext(session, System.Transactions.Transaction.Current); session.TransactionContext = transactionContext; logger.DebugFormat("enlisted into DTC transaction: {0}", transactionContext.AmbientTransation.IsolationLevel); session.AfterTransactionBegin(null); + foreach (var dependentSession in session.ConnectionManager.DependentSessions) + dependentSession.AfterTransactionBegin(null); TransactionCompletedEventHandler handler = null; @@ -55,12 +69,12 @@ public void EnlistInDistributedTransactionIfNeeded(ISessionImplementor session) { logger.Warn("Completed transaction was disposed, assuming transaction rollback", ode); } + session.ConnectionManager.AfterTransaction(); session.AfterTransactionCompletion(wasSuccessful, null); - if (transactionContext.ShouldCloseSessionOnDistributedTransactionCompleted) - { - session.CloseSessionFromDistributedTransaction(); - } - session.TransactionContext = null; + foreach (var dependentSession in session.ConnectionManager.DependentSessions) + dependentSession.AfterTransactionCompletion(wasSuccessful, null); + + Cleanup(session); } e.Transaction.TransactionCompleted -= handler; @@ -72,9 +86,27 @@ public void EnlistInDistributedTransactionIfNeeded(ISessionImplementor session) EnlistmentOptions.EnlistDuringPrepareRequired); } + private static void Cleanup(ISessionImplementor session) + { + foreach (var dependentSession in session.ConnectionManager.DependentSessions.ToList()) + { + if (dependentSession.TransactionContext?.ShouldCloseSessionOnDistributedTransactionCompleted ?? false) + // This change the enumerated collection. + dependentSession.CloseSessionFromDistributedTransaction(); + dependentSession.TransactionContext?.Dispose(); + dependentSession.TransactionContext = null; + } + if (session.TransactionContext.ShouldCloseSessionOnDistributedTransactionCompleted) + { + session.CloseSessionFromDistributedTransaction(); + } + session.TransactionContext.Dispose(); + session.TransactionContext = null; + } + public bool IsInDistributedActiveTransaction(ISessionImplementor session) { - var distributedTransactionContext = ((DistributedTransactionContext)session.TransactionContext); + var distributedTransactionContext = (DistributedTransactionContext)session.ConnectionManager.Session.TransactionContext; return distributedTransactionContext != null && distributedTransactionContext.IsInActiveTransaction; } @@ -114,18 +146,19 @@ void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment) { using (var tx = new TransactionScope(AmbientTransation)) { - sessionImplementor.BeforeTransactionCompletion(null); - if (sessionImplementor.FlushMode != FlushMode.Manual && sessionImplementor.ConnectionManager.IsConnected) + if (sessionImplementor.ConnectionManager.IsConnected) { using (sessionImplementor.ConnectionManager.FlushingFromDtcTransaction) { - logger.Debug(string.Format("[session-id={0}] Flushing from Dtc Transaction", sessionImplementor.SessionId)); - sessionImplementor.Flush(); + sessionImplementor.BeforeTransactionCompletion(null); + foreach (var dependentSession in sessionImplementor.ConnectionManager.DependentSessions) + dependentSession.BeforeTransactionCompletion(null); + + logger.Debug("prepared for DTC transaction"); + + tx.Complete(); } } - logger.Debug("prepared for DTC transaction"); - - tx.Complete(); } preparingEnlistment.Prepared(); } @@ -165,7 +198,10 @@ void IEnlistmentNotification.InDoubt(Enlistment enlistment) { using (new SessionIdLoggingContext(sessionImplementor.SessionId)) { + sessionImplementor.ConnectionManager.AfterTransaction(); sessionImplementor.AfterTransactionCompletion(false, null); + foreach (var dependentSession in sessionImplementor.ConnectionManager.DependentSessions) + dependentSession.AfterTransactionCompletion(false, null); logger.Debug("DTC transaction is in doubt"); enlistment.Done(); IsInActiveTransaction = false; @@ -180,5 +216,12 @@ public void Dispose() AmbientTransation.Dispose(); } } + + public class DependentContext : ITransactionContext + { + public bool ShouldCloseSessionOnDistributedTransactionCompleted { get; set; } + + public void Dispose() { } + } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Transaction/AdoTransaction.cs b/src/NHibernate/Transaction/AdoTransaction.cs index 162cba606e1..3b3f79ac5d6 100644 --- a/src/NHibernate/Transaction/AdoTransaction.cs +++ b/src/NHibernate/Transaction/AdoTransaction.cs @@ -154,6 +154,8 @@ public void Begin(IsolationLevel isolationLevel) rolledBack = false; session.AfterTransactionBegin(this); + foreach (var dependentSession in session.ConnectionManager.DependentSessions) + dependentSession.AfterTransactionBegin(this); } } @@ -161,8 +163,12 @@ private void AfterTransactionCompletion(bool successful) { using (new SessionIdLoggingContext(sessionId)) { + session.ConnectionManager.AfterTransaction(); session.AfterTransactionCompletion(successful, this); NotifyLocalSynchsAfterTransactionCompletion(successful); + foreach (var dependentSession in session.ConnectionManager.DependentSessions) + dependentSession.AfterTransactionCompletion(successful, this); + session = null; begun = false; } @@ -186,13 +192,10 @@ public void Commit() log.Debug("Start Commit"); - if (session.FlushMode != FlushMode.Manual) - { - session.Flush(); - } - - NotifyLocalSynchsBeforeTransactionCompletion(); session.BeforeTransactionCompletion(this); + NotifyLocalSynchsBeforeTransactionCompletion(); + foreach (var dependentSession in session.ConnectionManager.DependentSessions) + dependentSession.BeforeTransactionCompletion(this); try {