From 6ac5fa0031211f9c463ff8446e78134f7b4d37da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Mon, 8 Jan 2018 18:04:53 +0100 Subject: [PATCH 1/8] NH-3606 - open a stateless session from a session - Fixes #910 --- .../Async/SessionBuilder/Fixture.cs | 68 ++++++++++++++-- .../TransactionTest/TransactionFixture.cs | 31 +++++++- src/NHibernate.Test/DebugSessionFactory.cs | 3 +- src/NHibernate.Test/SessionBuilder/Fixture.cs | 77 ++++++++++++++----- .../TransactionTest/TransactionFixture.cs | 31 +++++++- src/NHibernate/Async/ISession.cs | 2 + src/NHibernate/ISession.cs | 17 ++++ .../ISharedStatelessSessionBuilder.cs | 57 ++++++++++++++ .../Impl/ISharedSessionCreationOptions.cs | 3 +- src/NHibernate/Impl/SessionFactoryImpl.cs | 26 +++++-- src/NHibernate/Impl/SessionImpl.cs | 69 +++++++++++++++++ src/NHibernate/Impl/StatelessSessionImpl.cs | 45 ++++++++++- 12 files changed, 389 insertions(+), 40 deletions(-) create mode 100644 src/NHibernate/ISharedStatelessSessionBuilder.cs diff --git a/src/NHibernate.Test/Async/SessionBuilder/Fixture.cs b/src/NHibernate.Test/Async/SessionBuilder/Fixture.cs index ac089680980..c481dde92b9 100644 --- a/src/NHibernate.Test/Async/SessionBuilder/Fixture.cs +++ b/src/NHibernate.Test/Async/SessionBuilder/Fixture.cs @@ -54,14 +54,28 @@ private void CanSetAutoJoinTransaction(T sb) where T : ISessionBuilder false, true); } + private void CanSetAutoJoinTransactionOnStateless(T sb) where T : IStatelessSessionBuilder + { + var options = DebugSessionFactory.GetCreationOptions(sb); + CanSetOnStateless( + sb, sb.AutoJoinTransaction, () => options.ShouldAutoJoinTransaction, + sb is ISharedStatelessSessionBuilder ssb ? ssb.AutoJoinTransaction : default(Func), + // initial value + true, + // values + false, true); + } + [Test] public async Task CanSetConnectionAsync() { var sb = Sfi.WithOptions(); await (CanSetConnectionAsync(sb)); + await (CanSetConnectionOnStatelessAsync(Sfi.WithStatelessOptions())); using (var s = sb.OpenSession()) { await (CanSetConnectionAsync(s.SessionWithOptions())); + await (CanSetConnectionOnStatelessAsync(s.StatelessSessionWithOptions())); } } @@ -109,12 +123,10 @@ public async Task CanSetConnectionAsync() } } - [Test] - public async Task CanSetConnectionOnStatelessAsync() + private async Task CanSetConnectionOnStatelessAsync(T sb, CancellationToken cancellationToken = default(CancellationToken)) where T : IStatelessSessionBuilder { - var sb = Sfi.WithStatelessOptions(); var sbType = sb.GetType().Name; - var conn = await (Sfi.ConnectionProvider.GetConnectionAsync(CancellationToken.None)); + var conn = await (Sfi.ConnectionProvider.GetConnectionAsync(cancellationToken)); try { var options = DebugSessionFactory.GetCreationOptions(sb); @@ -123,9 +135,31 @@ public async Task CanSetConnectionOnStatelessAsync() Assert.AreEqual(conn, options.UserSuppliedConnection, $"{sbType}: After call with a connection"); Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after call with a connection"); - fsb = sb.Connection(null); - Assert.IsNull(options.UserSuppliedConnection, $"{sbType}: After call with null"); - Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after call with null"); + if (sb is ISharedStatelessSessionBuilder ssb) + { + var sharedOptions = (ISharedSessionCreationOptions)options; + Assert.IsFalse(sharedOptions.IsTransactionCoordinatorShared, $"{sbType}: Transaction coordinator shared before sharing"); + Assert.IsNull(sharedOptions.ConnectionManager, $"{sbType}: Connection manager shared before sharing"); + + var fssb = ssb.Connection(); + // Sharing connection shares the connection manager, not the connection. + Assert.IsNull(options.UserSuppliedConnection, $"{sbType}: After call with previous session connection"); + Assert.IsTrue(sharedOptions.IsTransactionCoordinatorShared, $"{sbType}: Transaction coordinator not shared after sharing"); + Assert.IsNotNull(sharedOptions.ConnectionManager, $"{sbType}: Connection manager not shared after sharing"); + Assert.AreEqual(sb, fssb, $"{sbType}: Unexpected fluent return on shared"); + + fsb = sb.Connection(null); + Assert.IsNull(options.UserSuppliedConnection, $"{sbType}: After call with null"); + Assert.IsFalse(sharedOptions.IsTransactionCoordinatorShared, $"{sbType}: Transaction coordinator shared after un-sharing"); + Assert.IsNull(sharedOptions.ConnectionManager, $"{sbType}: Connection manager shared after un-sharing"); + Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after un-sharing"); + } + else + { + fsb = sb.Connection(null); + Assert.IsNull(options.UserSuppliedConnection, $"{sbType}: After call with null"); + Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after call with null"); + } } finally { @@ -218,5 +252,25 @@ private void CanSet(T sb, Func setter, Func getter, Func( + T sb, Func setter, Func getter, Func shared, V initialValue, + params V[] values) where T : IStatelessSessionBuilder + { + var sbType = sb.GetType().Name; + Assert.AreEqual(initialValue, getter(), $"{sbType}: Initial value"); + if (shared != null) + { + var fssb = shared(); + Assert.AreEqual(values.Last(), getter(), $"{sbType}: After call with shared setting"); + Assert.AreEqual(sb, fssb, $"{sbType}: Unexpected fluent return on shared"); + } + foreach (var value in values) + { + var fsb = setter(value); + Assert.AreEqual(value, getter(), $"{sbType}: After call with {value}"); + Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after call with {value}"); + } + } } } diff --git a/src/NHibernate.Test/Async/TransactionTest/TransactionFixture.cs b/src/NHibernate.Test/Async/TransactionTest/TransactionFixture.cs index 2e94735f7b7..ab4a0091a75 100644 --- a/src/NHibernate.Test/Async/TransactionTest/TransactionFixture.cs +++ b/src/NHibernate.Test/Async/TransactionTest/TransactionFixture.cs @@ -170,6 +170,35 @@ public async Task FlushFromTransactionAppliesToSharingSessionAsync() } } + [Test] + public async Task FlushFromTransactionAppliesToSharingStatelessSessionAsync() + { + using (var s = OpenSession()) + { + var builder = s.StatelessSessionWithOptions().Connection(); + + using (var s1 = builder.OpenStatelessSession()) + using (var s2 = builder.OpenStatelessSession()) + using (var t = s.BeginTransaction()) + { + var p1 = new Person(); + var p2 = new Person(); + var p3 = new Person(); + await (s1.InsertAsync(p1)); + await (s2.InsertAsync(p2)); + await (s.SaveAsync(p3)); + await (t.CommitAsync()); + } + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + Assert.That(await (s.Query().CountAsync()), Is.EqualTo(3)); + await (t.CommitAsync()); + } + } + // Taken and adjusted from NH1632 When_commiting_items_in_DTC_transaction_will_add_items_to_2nd_level_cache [Test] public async Task WhenCommittingItemsWillAddThemTo2ndLevelCacheAsync() @@ -210,4 +239,4 @@ public async Task WhenCommittingItemsWillAddThemTo2ndLevelCacheAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/DebugSessionFactory.cs b/src/NHibernate.Test/DebugSessionFactory.cs index 041003c9c74..99331bef6d9 100644 --- a/src/NHibernate.Test/DebugSessionFactory.cs +++ b/src/NHibernate.Test/DebugSessionFactory.cs @@ -386,7 +386,8 @@ public static ISessionCreationOptions GetCreationOptions(ISessionBuilder s public static ISessionCreationOptions GetCreationOptions(IStatelessSessionBuilder sessionBuilder) { - return ((StatelessSessionBuilder)sessionBuilder).CreationOptions; + return (sessionBuilder as StatelessSessionBuilder)?.CreationOptions ?? + (ISessionCreationOptions)sessionBuilder; } internal class SessionBuilder : ISessionBuilder diff --git a/src/NHibernate.Test/SessionBuilder/Fixture.cs b/src/NHibernate.Test/SessionBuilder/Fixture.cs index dfd17982e68..e3afc27b87d 100644 --- a/src/NHibernate.Test/SessionBuilder/Fixture.cs +++ b/src/NHibernate.Test/SessionBuilder/Fixture.cs @@ -47,9 +47,11 @@ public void CanSetAutoJoinTransaction() { var sb = Sfi.WithOptions(); CanSetAutoJoinTransaction(sb); + CanSetAutoJoinTransactionOnStateless(Sfi.WithStatelessOptions()); using (var s = sb.OpenSession()) { CanSetAutoJoinTransaction(s.SessionWithOptions()); + CanSetAutoJoinTransactionOnStateless(s.StatelessSessionWithOptions()); } } @@ -64,21 +66,16 @@ private void CanSetAutoJoinTransaction(T sb) where T : ISessionBuilder false, true); } - [Test] - public void CanSetAutoJoinTransactionOnStateless() + private void CanSetAutoJoinTransactionOnStateless(T sb) where T : IStatelessSessionBuilder { - var sb = Sfi.WithStatelessOptions(); - - var sbType = sb.GetType().Name; var options = DebugSessionFactory.GetCreationOptions(sb); - Assert.That(options.ShouldAutoJoinTransaction, Is.True, $"{sbType}: Initial value"); - var fsb = sb.AutoJoinTransaction(false); - Assert.That(options.ShouldAutoJoinTransaction, Is.False, $"{sbType}: After call with false"); - Assert.That(fsb, Is.SameAs(sb), $"{sbType}: Unexpected fluent return after call with false"); - - fsb = sb.AutoJoinTransaction(true); - Assert.That(options.ShouldAutoJoinTransaction, Is.True, $"{sbType}: After call with true"); - Assert.That(fsb, Is.SameAs(sb), $"{sbType}: Unexpected fluent return after call with true"); + CanSetOnStateless( + sb, sb.AutoJoinTransaction, () => options.ShouldAutoJoinTransaction, + sb is ISharedStatelessSessionBuilder ssb ? ssb.AutoJoinTransaction : default(Func), + // initial value + true, + // values + false, true); } [Test] @@ -86,9 +83,11 @@ public void CanSetConnection() { var sb = Sfi.WithOptions(); CanSetConnection(sb); + CanSetConnectionOnStateless(Sfi.WithStatelessOptions()); using (var s = sb.OpenSession()) { CanSetConnection(s.SessionWithOptions()); + CanSetConnectionOnStateless(s.StatelessSessionWithOptions()); } } @@ -136,10 +135,8 @@ private void CanSetConnection(T sb) where T : ISessionBuilder } } - [Test] - public void CanSetConnectionOnStateless() + private void CanSetConnectionOnStateless(T sb) where T : IStatelessSessionBuilder { - var sb = Sfi.WithStatelessOptions(); var sbType = sb.GetType().Name; var conn = Sfi.ConnectionProvider.GetConnection(); try @@ -150,9 +147,31 @@ public void CanSetConnectionOnStateless() Assert.AreEqual(conn, options.UserSuppliedConnection, $"{sbType}: After call with a connection"); Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after call with a connection"); - fsb = sb.Connection(null); - Assert.IsNull(options.UserSuppliedConnection, $"{sbType}: After call with null"); - Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after call with null"); + if (sb is ISharedStatelessSessionBuilder ssb) + { + var sharedOptions = (ISharedSessionCreationOptions)options; + Assert.IsFalse(sharedOptions.IsTransactionCoordinatorShared, $"{sbType}: Transaction coordinator shared before sharing"); + Assert.IsNull(sharedOptions.ConnectionManager, $"{sbType}: Connection manager shared before sharing"); + + var fssb = ssb.Connection(); + // Sharing connection shares the connection manager, not the connection. + Assert.IsNull(options.UserSuppliedConnection, $"{sbType}: After call with previous session connection"); + Assert.IsTrue(sharedOptions.IsTransactionCoordinatorShared, $"{sbType}: Transaction coordinator not shared after sharing"); + Assert.IsNotNull(sharedOptions.ConnectionManager, $"{sbType}: Connection manager not shared after sharing"); + Assert.AreEqual(sb, fssb, $"{sbType}: Unexpected fluent return on shared"); + + fsb = sb.Connection(null); + Assert.IsNull(options.UserSuppliedConnection, $"{sbType}: After call with null"); + Assert.IsFalse(sharedOptions.IsTransactionCoordinatorShared, $"{sbType}: Transaction coordinator shared after un-sharing"); + Assert.IsNull(sharedOptions.ConnectionManager, $"{sbType}: Connection manager shared after un-sharing"); + Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after un-sharing"); + } + else + { + fsb = sb.Connection(null); + Assert.IsNull(options.UserSuppliedConnection, $"{sbType}: After call with null"); + Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after call with null"); + } } finally { @@ -259,5 +278,25 @@ private void CanSet(T sb, Func setter, Func getter, Func( + T sb, Func setter, Func getter, Func shared, V initialValue, + params V[] values) where T : IStatelessSessionBuilder + { + var sbType = sb.GetType().Name; + Assert.AreEqual(initialValue, getter(), $"{sbType}: Initial value"); + if (shared != null) + { + var fssb = shared(); + Assert.AreEqual(values.Last(), getter(), $"{sbType}: After call with shared setting"); + Assert.AreEqual(sb, fssb, $"{sbType}: Unexpected fluent return on shared"); + } + foreach (var value in values) + { + var fsb = setter(value); + Assert.AreEqual(value, getter(), $"{sbType}: After call with {value}"); + Assert.AreEqual(sb, fsb, $"{sbType}: Unexpected fluent return after call with {value}"); + } + } } } diff --git a/src/NHibernate.Test/TransactionTest/TransactionFixture.cs b/src/NHibernate.Test/TransactionTest/TransactionFixture.cs index 7e49744ed50..b06d8d76326 100644 --- a/src/NHibernate.Test/TransactionTest/TransactionFixture.cs +++ b/src/NHibernate.Test/TransactionTest/TransactionFixture.cs @@ -173,6 +173,35 @@ public void FlushFromTransactionAppliesToSharingSession() } } + [Test] + public void FlushFromTransactionAppliesToSharingStatelessSession() + { + using (var s = OpenSession()) + { + var builder = s.StatelessSessionWithOptions().Connection(); + + using (var s1 = builder.OpenStatelessSession()) + using (var s2 = builder.OpenStatelessSession()) + using (var t = s.BeginTransaction()) + { + var p1 = new Person(); + var p2 = new Person(); + var p3 = new Person(); + s1.Insert(p1); + s2.Insert(p2); + s.Save(p3); + t.Commit(); + } + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + Assert.That(s.Query().Count(), Is.EqualTo(3)); + t.Commit(); + } + } + // Taken and adjusted from NH1632 When_commiting_items_in_DTC_transaction_will_add_items_to_2nd_level_cache [Test] public void WhenCommittingItemsWillAddThemTo2ndLevelCache() @@ -213,4 +242,4 @@ public void WhenCommittingItemsWillAddThemTo2ndLevelCache() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/ISession.cs b/src/NHibernate/Async/ISession.cs index 80b1232f940..45cc1b9f9e3 100644 --- a/src/NHibernate/Async/ISession.cs +++ b/src/NHibernate/Async/ISession.cs @@ -16,6 +16,7 @@ using NHibernate.Engine; using NHibernate.Event; using NHibernate.Event.Default; +using NHibernate.Impl; using NHibernate.Stat; using NHibernate.Type; @@ -23,6 +24,7 @@ namespace NHibernate { using System.Threading.Tasks; using System.Threading; + public partial interface ISession : IDisposable { diff --git a/src/NHibernate/ISession.cs b/src/NHibernate/ISession.cs index 0ac9aafc3ed..9306c62332e 100644 --- a/src/NHibernate/ISession.cs +++ b/src/NHibernate/ISession.cs @@ -6,11 +6,28 @@ using NHibernate.Engine; using NHibernate.Event; using NHibernate.Event.Default; +using NHibernate.Impl; using NHibernate.Stat; using NHibernate.Type; namespace NHibernate { + // 6.0 TODO: Convert to interface methods + public static class SessionExtensions + { + /// + /// Obtain a builder with the ability to grab certain information from + /// this session. The built IStatelessSession will require its own disposal. + /// + /// The session from which to build a stateless session. + /// The session builder. + public static ISharedStatelessSessionBuilder StatelessSessionWithOptions(this ISession session) + { + var impl = session as SessionImpl ?? throw new NotSupportedException("Only SessionImpl sessions are supported."); + return impl.StatelessSessionWithOptions(); + } + } + /// /// The main runtime interface between a .NET application and NHibernate. This is the central /// API class abstracting the notion of a persistence service. diff --git a/src/NHibernate/ISharedStatelessSessionBuilder.cs b/src/NHibernate/ISharedStatelessSessionBuilder.cs new file mode 100644 index 00000000000..349de35d8b4 --- /dev/null +++ b/src/NHibernate/ISharedStatelessSessionBuilder.cs @@ -0,0 +1,57 @@ +using System.Data.Common; +using NHibernate.Connection; + +namespace NHibernate +{ + // NH different implementation: will not try to support covariant return type for specializations + // until it is needed. + /// + /// Specialized with access to stuff from another session. + /// + // 6.0 TODO: implement covariance the way used for ISharedSessionBuilder + public interface ISharedStatelessSessionBuilder : IStatelessSessionBuilder + { + #region 6.0 TODO: implement covariance the way used for ISharedSessionBuilder + + /// + /// Adds a specific connection to the session options. + /// + /// The connection to use. + /// , for method chaining. + /// + /// Note that the second-level cache will be disabled if you + /// supply a ADO.NET connection. NHibernate will not be able to track + /// any statements you might have executed in the same transaction. + /// Consider implementing your own . + /// + new ISharedStatelessSessionBuilder Connection(DbConnection connection); + + /// + /// Should the session be automatically enlisted in ambient system transaction? + /// Enabled by default. Disabling it does not prevent connections having auto-enlistment + /// enabled to get enlisted in current ambient transaction when opened. + /// + /// Should the session be automatically explicitly + /// enlisted in ambient transaction. + /// , for method chaining. + new ISharedStatelessSessionBuilder AutoJoinTransaction(bool autoJoinTransaction); + + #endregion + + /// + /// 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. + /// Causes specified ConnectionReleaseMode and AutoJoinTransaction to be ignored and + /// replaced by those of the original session. + /// + /// , for method chaining. + ISharedStatelessSessionBuilder Connection(); + + /// + /// Signifies that the AutoJoinTransaction flag from the original session should be used to create the new session. + /// + /// , for method chaining. + ISharedStatelessSessionBuilder AutoJoinTransaction(); + } +} diff --git a/src/NHibernate/Impl/ISharedSessionCreationOptions.cs b/src/NHibernate/Impl/ISharedSessionCreationOptions.cs index a0fd77451f0..25c0a57d6f5 100644 --- a/src/NHibernate/Impl/ISharedSessionCreationOptions.cs +++ b/src/NHibernate/Impl/ISharedSessionCreationOptions.cs @@ -7,6 +7,7 @@ namespace NHibernate.Impl /// some part of the "transaction context" of another Session. /// /// + /// public interface ISharedSessionCreationOptions : ISessionCreationOptions { // NH note: naming "adjusted" for converting Java methods to properties while avoiding conflicts with @@ -15,4 +16,4 @@ public interface ISharedSessionCreationOptions : ISessionCreationOptions // NH different implementation: need to port Hibernate transaction management. ConnectionManager ConnectionManager { get; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index 78514bffb33..ff9e630ca99 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -1390,10 +1390,19 @@ public virtual T FlushMode(FlushMode flushMode) } } - // NH different implementation: will not try to support covariant return type for specializations - // until it is needed. - internal class StatelessSessionBuilderImpl : IStatelessSessionBuilder, ISessionCreationOptions + // NH specific: implementing return type covariance with interface is a mess in .Net. + internal class StatelessSessionBuilderImpl : StatelessSessionBuilderImpl + { + public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) : base(sessionFactory) + { + SetSelf(this); + } + } + + internal class StatelessSessionBuilderImpl : IStatelessSessionBuilder, ISessionCreationOptions where T : IStatelessSessionBuilder { + // NH specific: implementing return type covariance with interface is a mess in .Net. + private T _this; private readonly SessionFactoryImpl _sessionFactory; public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) @@ -1401,18 +1410,23 @@ public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) _sessionFactory = sessionFactory; } + protected void SetSelf(T self) + { + _this = self; + } + public virtual IStatelessSession OpenStatelessSession() => new StatelessSessionImpl(_sessionFactory, this); - public IStatelessSessionBuilder Connection(DbConnection connection) + public virtual IStatelessSessionBuilder Connection(DbConnection connection) { UserSuppliedConnection = connection; - return this; + return _this; } public IStatelessSessionBuilder AutoJoinTransaction(bool autoJoinTransaction) { ShouldAutoJoinTransaction = autoJoinTransaction; - return this; + return _this; } public FlushMode InitialSessionFlushMode => FlushMode.Always; diff --git a/src/NHibernate/Impl/SessionImpl.cs b/src/NHibernate/Impl/SessionImpl.cs index ae48d621780..a6fc5bade59 100644 --- a/src/NHibernate/Impl/SessionImpl.cs +++ b/src/NHibernate/Impl/SessionImpl.cs @@ -1899,6 +1899,11 @@ public ISharedSessionBuilder SessionWithOptions() return new SharedSessionBuilderImpl(this); } + public ISharedStatelessSessionBuilder StatelessSessionWithOptions() + { + return new SharedStatelessSessionBuilderImpl(this); + } + public void Clear() { using (BeginProcess()) @@ -2519,5 +2524,69 @@ public override ISharedSessionBuilder Connection(DbConnection connection) // NH different implementation: need to port Hibernate transaction management. public ConnectionManager ConnectionManager => _shareTransactionContext ? _session.ConnectionManager : null; } + + // NH specific: allow to build a stateless session from a normal session. + private class SharedStatelessSessionBuilderImpl : SessionFactoryImpl.StatelessSessionBuilderImpl, + ISharedStatelessSessionBuilder, ISharedSessionCreationOptions + { + private readonly SessionImpl _session; + private bool _shareTransactionContext; + + public SharedStatelessSessionBuilderImpl(SessionImpl session) + : base((SessionFactoryImpl)session.Factory) + { + _session = session; + SetSelf(this); + } + + #region 6.0 TODO: implement covariance the way used for ISharedSessionBuilder + + ISharedStatelessSessionBuilder ISharedStatelessSessionBuilder.AutoJoinTransaction(bool autoJoinTransaction) + { + AutoJoinTransaction(autoJoinTransaction); + return this; + } + + ISharedStatelessSessionBuilder ISharedStatelessSessionBuilder.Connection(DbConnection connection) + { + Connection(connection); + return this; + } + + #endregion + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SharedSessionBuilder + + public virtual ISharedStatelessSessionBuilder Connection() + { + // Ensure any previously user supplied connection is removed. + base.Connection(null); + // We share the connection manager + _shareTransactionContext = true; + return this; + } + + public virtual ISharedStatelessSessionBuilder AutoJoinTransaction() + { + AutoJoinTransaction(_session.ConnectionManager.ShouldAutoJoinTransaction); + return this; + } + + // NH different implementation, avoid an error case. + public override IStatelessSessionBuilder Connection(DbConnection connection) + { + _shareTransactionContext = false; + return base.Connection(connection); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SharedSessionCreationOptions + + public virtual bool IsTransactionCoordinatorShared => _shareTransactionContext; + + // NH different implementation: need to port Hibernate transaction management. + public ConnectionManager ConnectionManager => _shareTransactionContext ? _session.ConnectionManager : null; + } } } diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 7127f8f786f..68d944f3598 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -34,14 +34,31 @@ public partial class StatelessSessionImpl : AbstractSessionImpl, IStatelessSessi [NonSerialized] private readonly StatefulPersistenceContext temporaryPersistenceContext; + [NonSerialized] + private readonly bool _transactionCoordinatorShared; + internal StatelessSessionImpl(SessionFactoryImpl factory, ISessionCreationOptions options) : base(factory, options) { using (BeginContext()) { temporaryPersistenceContext = new StatefulPersistenceContext(this); - connectionManager = new ConnectionManager(this, options.UserSuppliedConnection, ConnectionReleaseMode.AfterTransaction, - EmptyInterceptor.Instance, options.ShouldAutoJoinTransaction); + + if (options is ISharedSessionCreationOptions sharedOptions && sharedOptions.IsTransactionCoordinatorShared) + { + // NH specific implementation: need to port Hibernate transaction management. + _transactionCoordinatorShared = true; + if (options.UserSuppliedConnection != null) + throw new SessionException("Cannot simultaneously share transaction context and specify connection"); + connectionManager = sharedOptions.ConnectionManager; + connectionManager.AddDependentSession(this); + } + else + { + connectionManager = new ConnectionManager( + this, options.UserSuppliedConnection, ConnectionReleaseMode.AfterTransaction, + EmptyInterceptor.Instance, options.ShouldAutoJoinTransaction); + } if (log.IsDebugEnabled()) { @@ -455,7 +472,10 @@ public void ManagedClose() { throw new SessionException("Session was already closed!"); } - ConnectionManager.Close(); + if (_transactionCoordinatorShared) + connectionManager.RemoveDependentSession(this); + else + connectionManager.Close(); SetClosed(); } } @@ -786,7 +806,17 @@ public IQueryOver QueryOver(Expression> alias) where T : class /// A NHibernate transaction public ITransaction BeginTransaction() { - return BeginTransaction(IsolationLevel.Unspecified); + using (BeginProcess()) + { + if (_transactionCoordinatorShared) + { + // Todo : should seriously consider not allowing a txn to begin from a child session + // can always route the request to the root session... + log.Warn("Transaction started on non-root session"); + } + + return connectionManager.BeginTransaction(); + } } /// @@ -798,6 +828,13 @@ public ITransaction BeginTransaction(IsolationLevel isolationLevel) { using (BeginProcess()) { + if (_transactionCoordinatorShared) + { + // Todo : should seriously consider not allowing a txn to begin from a child session + // can always route the request to the root session... + log.Warn("Transaction started on non-root session"); + } + return connectionManager.BeginTransaction(isolationLevel); } } From 875f28dcddbfc00604791e4cb6f7e2bb5bb77a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Mon, 22 Jan 2018 16:48:56 +0100 Subject: [PATCH 2/8] Minimal move of duplicated code. --- .../Async/Impl/AbstractSessionImpl.cs | 1 + src/NHibernate/Async/Impl/SessionImpl.cs | 1 - .../Async/Impl/StatelessSessionImpl.cs | 2 - src/NHibernate/Impl/AbstractSessionImpl.cs | 79 ++++++++++++++++-- src/NHibernate/Impl/SessionImpl.cs | 83 ++++--------------- src/NHibernate/Impl/StatelessSessionImpl.cs | 82 ++---------------- 6 files changed, 95 insertions(+), 153 deletions(-) diff --git a/src/NHibernate/Async/Impl/AbstractSessionImpl.cs b/src/NHibernate/Async/Impl/AbstractSessionImpl.cs index 72a5433f7f0..95a6fd28a4d 100644 --- a/src/NHibernate/Async/Impl/AbstractSessionImpl.cs +++ b/src/NHibernate/Async/Impl/AbstractSessionImpl.cs @@ -11,6 +11,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Data; using System.Data.Common; using System.Linq; using NHibernate.AdoNet; diff --git a/src/NHibernate/Async/Impl/SessionImpl.cs b/src/NHibernate/Async/Impl/SessionImpl.cs index d3d7a100bf4..5ab05babfb5 100644 --- a/src/NHibernate/Async/Impl/SessionImpl.cs +++ b/src/NHibernate/Async/Impl/SessionImpl.cs @@ -11,7 +11,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Data; using System.Data.Common; using System.Linq.Expressions; using System.Runtime.Serialization; diff --git a/src/NHibernate/Async/Impl/StatelessSessionImpl.cs b/src/NHibernate/Async/Impl/StatelessSessionImpl.cs index 1872e963450..70a8c800dcf 100644 --- a/src/NHibernate/Async/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Async/Impl/StatelessSessionImpl.cs @@ -11,10 +11,8 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Data; using System.Data.Common; using System.Linq.Expressions; -using NHibernate.AdoNet; using NHibernate.Cache; using NHibernate.Collection; using NHibernate.Criterion; diff --git a/src/NHibernate/Impl/AbstractSessionImpl.cs b/src/NHibernate/Impl/AbstractSessionImpl.cs index 7d946e7a978..ebe2f5d233b 100644 --- a/src/NHibernate/Impl/AbstractSessionImpl.cs +++ b/src/NHibernate/Impl/AbstractSessionImpl.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Data; using System.Data.Common; using System.Linq; using NHibernate.AdoNet; @@ -31,6 +32,8 @@ public abstract partial class AbstractSessionImpl : ISessionImplementor private bool closed; + protected bool TransactionCoordinatorShared { get; } + public ITransactionContext TransactionContext { get; set; @@ -38,7 +41,7 @@ public ITransactionContext TransactionContext private bool isAlreadyDisposed; - private static readonly INHibernateLogger logger = NHibernateLogger.For(typeof(AbstractSessionImpl)); + private static readonly INHibernateLogger Log = NHibernateLogger.For(typeof(AbstractSessionImpl)); public Guid SessionId { get; } @@ -46,11 +49,34 @@ internal AbstractSessionImpl() { } protected internal AbstractSessionImpl(ISessionFactoryImplementor factory, ISessionCreationOptions options) { - _factory = factory; - Timestamp = factory.Settings.CacheProvider.NextTimestamp(); - _flushMode = options.InitialSessionFlushMode; - Interceptor = options.SessionInterceptor ?? EmptyInterceptor.Instance; SessionId = factory.Settings.TrackSessionId ? Guid.NewGuid() : Guid.Empty; + using (BeginContext()) + { + _factory = factory; + Timestamp = factory.Settings.CacheProvider.NextTimestamp(); + _flushMode = options.InitialSessionFlushMode; + Interceptor = options.SessionInterceptor ?? EmptyInterceptor.Instance; + + if (options is ISharedSessionCreationOptions sharedOptions && sharedOptions.IsTransactionCoordinatorShared) + { + // NH specific implementation: need to port Hibernate transaction management. + TransactionCoordinatorShared = true; + if (options.UserSuppliedConnection != null) + throw new SessionException("Cannot simultaneously share transaction context and specify connection"); + var connectionManager = sharedOptions.ConnectionManager; + connectionManager.AddDependentSession(this); + ConnectionManager = connectionManager; + } + else + { + ConnectionManager = new ConnectionManager( + this, + options.UserSuppliedConnection, + options.SessionConnectionReleaseMode, + Interceptor, + options.ShouldAutoJoinTransaction); + } + } } #region ISessionImplementor Members @@ -218,9 +244,11 @@ public virtual IQuery GetNamedSQLQuery(string name) } } + // 6.0 TODO: remove virtual. + public virtual ConnectionManager ConnectionManager { get; protected set; } + public abstract IQueryTranslator[] GetQueries(IQueryExpression query, bool scalar); public abstract EventListeners Listeners { get; } - public abstract ConnectionManager ConnectionManager { get; } public abstract bool IsEventSource { get; } public abstract object GetEntityUsingInterceptor(EntityKey key); public abstract IPersistenceContext PersistenceContext { get; } @@ -471,6 +499,45 @@ protected void AfterOperation(bool success) } } + /// + /// Begin a NHibernate transaction + /// + /// A NHibernate transaction + public ITransaction BeginTransaction() + { + using (BeginProcess()) + { + if (TransactionCoordinatorShared) + { + // Todo : should seriously consider not allowing a txn to begin from a child session + // can always route the request to the root session... + Log.Warn("Transaction started on non-root session"); + } + + return ConnectionManager.BeginTransaction(); + } + } + + /// + /// Begin a NHibernate transaction with the specified isolation level + /// + /// The isolation level + /// A NHibernate transaction + public ITransaction BeginTransaction(IsolationLevel isolationLevel) + { + using (BeginProcess()) + { + if (TransactionCoordinatorShared) + { + // Todo : should seriously consider not allowing a txn to begin from a child session + // can always route the request to the root session... + Log.Warn("Transaction started on non-root session"); + } + + return ConnectionManager.BeginTransaction(isolationLevel); + } + } + protected void EnlistInAmbientTransactionIfNeeded() { _factory.TransactionFactory.EnlistInSystemTransactionIfNeeded(this); diff --git a/src/NHibernate/Impl/SessionImpl.cs b/src/NHibernate/Impl/SessionImpl.cs index a6fc5bade59..f2c34e43e1b 100644 --- a/src/NHibernate/Impl/SessionImpl.cs +++ b/src/NHibernate/Impl/SessionImpl.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Data; using System.Data.Common; using System.Linq.Expressions; using System.Runtime.Serialization; @@ -56,8 +55,6 @@ public sealed partial class SessionImpl : AbstractSessionImpl, IEventSource, ISe [NonSerialized] private int _suspendAutoFlushCount; - private readonly ConnectionManager connectionManager; - [NonSerialized] private readonly IDictionary enabledFilters = new Dictionary(); @@ -71,8 +68,6 @@ public sealed partial class SessionImpl : AbstractSessionImpl, IEventSource, ISe private readonly bool autoCloseSessionEnabled; [NonSerialized] private readonly ConnectionReleaseMode connectionReleaseMode; - [NonSerialized] - private readonly bool _transactionCoordinatorShared; #region System.Runtime.Serialization.ISerializable Members @@ -104,7 +99,7 @@ private SessionImpl(SerializationInfo info, StreamingContext context) enabledFilters = (IDictionary)info.GetValue("enabledFilters", typeof(Dictionary)); enabledFilterNames = (List)info.GetValue("enabledFilterNames", typeof(List)); - connectionManager = (ConnectionManager)info.GetValue("connectionManager", typeof(ConnectionManager)); + ConnectionManager = (ConnectionManager)info.GetValue("connectionManager", typeof(ConnectionManager)); } /// @@ -122,11 +117,11 @@ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext contex { log.Debug("writting session to serializer"); - if (!connectionManager.IsReadyForSerialization) + if (!ConnectionManager.IsReadyForSerialization) { throw new InvalidOperationException("Cannot serialize a Session while connected"); } - if (_transactionCoordinatorShared) + if (TransactionCoordinatorShared) { throw new InvalidOperationException("Cannot serialize a Session sharing its transaction coordinator"); } @@ -143,7 +138,7 @@ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext contex info.AddValue("enabledFilters", enabledFilters, typeof(IDictionary)); info.AddValue("enabledFilterNames", enabledFilterNames, typeof(List)); - info.AddValue("connectionManager", connectionManager, typeof(ConnectionManager)); + info.AddValue("connectionManager", ConnectionManager, typeof(ConnectionManager)); } #endregion @@ -194,21 +189,6 @@ internal SessionImpl(SessionFactoryImpl factory, ISessionCreationOptions options listeners = factory.EventListeners; connectionReleaseMode = options.SessionConnectionReleaseMode; - if (options is ISharedSessionCreationOptions sharedOptions && sharedOptions.IsTransactionCoordinatorShared) - { - // NH specific implementation: need to port Hibernate transaction management. - _transactionCoordinatorShared = true; - if (options.UserSuppliedConnection != null) - throw new SessionException("Cannot simultaneously share transaction context and specify connection"); - connectionManager = sharedOptions.ConnectionManager; - connectionManager.AddDependentSession(this); - } - else - { - connectionManager = new ConnectionManager( - this, options.UserSuppliedConnection, connectionReleaseMode, Interceptor, options.ShouldAutoJoinTransaction); - } - if (factory.Statistics.IsStatisticsEnabled) { factory.StatisticsImplementor.OpenSession(); @@ -255,7 +235,7 @@ public override IBatcher Batcher get { CheckAndUpdateSessionStatus(); - return connectionManager.Batcher; + return ConnectionManager.Batcher; } } @@ -298,9 +278,9 @@ public DbConnection Close() try { - if (!_transactionCoordinatorShared) - return connectionManager.Close(); - connectionManager.RemoveDependentSession(this); + if (!TransactionCoordinatorShared) + return ConnectionManager.Close(); + ConnectionManager.RemoveDependentSession(this); return null; } finally @@ -1319,39 +1299,9 @@ public void Refresh(object obj, LockMode lockMode) } } - public ITransaction BeginTransaction(IsolationLevel isolationLevel) - { - using (BeginProcess()) - { - if (_transactionCoordinatorShared) - { - // Todo : should seriously consider not allowing a txn to begin from a child session - // can always route the request to the root session... - log.Warn("Transaction started on non-root session"); - } - - return connectionManager.BeginTransaction(isolationLevel); - } - } - - public ITransaction BeginTransaction() - { - using (BeginProcess()) - { - if (_transactionCoordinatorShared) - { - // Todo : should seriously consider not allowing a txn to begin from a child session - // can always route the request to the root session... - log.Warn("Transaction started on non-root session"); - } - - return connectionManager.BeginTransaction(); - } - } - public ITransaction Transaction { - get { return connectionManager.Transaction; } + get { return ConnectionManager.Transaction; } } /// @@ -1507,7 +1457,7 @@ public override void InitializeCollection(IPersistentCollection collection, bool public override DbConnection Connection { - get { return connectionManager.GetConnection(); } + get { return ConnectionManager.GetConnection(); } } /// @@ -1523,7 +1473,7 @@ public override DbConnection Connection /// public override bool IsConnected { - get { return connectionManager.IsConnected; } + get { return ConnectionManager.IsConnected; } } /// @@ -1532,7 +1482,7 @@ public DbConnection Disconnect() using (BeginProcess()) { log.Debug("disconnecting session"); - return connectionManager.Disconnect(); + return ConnectionManager.Disconnect(); } } @@ -1541,7 +1491,7 @@ public void Reconnect() using (BeginProcess()) { log.Debug("reconnecting session"); - connectionManager.Reconnect(); + ConnectionManager.Reconnect(); } } @@ -1550,7 +1500,7 @@ public void Reconnect(DbConnection conn) using (BeginProcess()) { log.Debug("reconnecting session"); - connectionManager.Reconnect(conn); + ConnectionManager.Reconnect(conn); } } @@ -2038,11 +1988,6 @@ private string[] ParseFilterParameterName(string filterParameterName) return new[] { filterName, parameterName }; } - public override ConnectionManager ConnectionManager - { - get { return connectionManager; } - } - public IMultiQuery CreateMultiQuery() { using (BeginProcess()) diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 68d944f3598..2434f2055c9 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -1,10 +1,8 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Data; using System.Data.Common; using System.Linq.Expressions; -using NHibernate.AdoNet; using NHibernate.Cache; using NHibernate.Collection; using NHibernate.Criterion; @@ -28,37 +26,15 @@ public partial class StatelessSessionImpl : AbstractSessionImpl, IStatelessSessi { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(StatelessSessionImpl)); - [NonSerialized] - private readonly ConnectionManager connectionManager; - [NonSerialized] private readonly StatefulPersistenceContext temporaryPersistenceContext; - [NonSerialized] - private readonly bool _transactionCoordinatorShared; - internal StatelessSessionImpl(SessionFactoryImpl factory, ISessionCreationOptions options) : base(factory, options) { using (BeginContext()) { temporaryPersistenceContext = new StatefulPersistenceContext(this); - - if (options is ISharedSessionCreationOptions sharedOptions && sharedOptions.IsTransactionCoordinatorShared) - { - // NH specific implementation: need to port Hibernate transaction management. - _transactionCoordinatorShared = true; - if (options.UserSuppliedConnection != null) - throw new SessionException("Cannot simultaneously share transaction context and specify connection"); - connectionManager = sharedOptions.ConnectionManager; - connectionManager.AddDependentSession(this); - } - else - { - connectionManager = new ConnectionManager( - this, options.UserSuppliedConnection, ConnectionReleaseMode.AfterTransaction, - EmptyInterceptor.Instance, options.ShouldAutoJoinTransaction); - } if (log.IsDebugEnabled()) { @@ -117,7 +93,7 @@ public override IBatcher Batcher get { CheckAndUpdateSessionStatus(); - return connectionManager.Batcher; + return ConnectionManager.Batcher; } } @@ -328,11 +304,6 @@ public override EventListeners Listeners get { throw new NotSupportedException(); } } - public override ConnectionManager ConnectionManager - { - get { return connectionManager; } - } - public override bool IsEventSource { get { return false; } @@ -364,7 +335,7 @@ public override bool IsOpen public override bool IsConnected { - get { return connectionManager.IsConnected; } + get { return ConnectionManager.IsConnected; } } public override FlushMode FlushMode @@ -396,7 +367,7 @@ public override string GuessEntityName(object entity) public override DbConnection Connection { - get { return connectionManager.GetConnection(); } + get { return ConnectionManager.GetConnection(); } } public IStatelessSession SetBatchSize(int batchSize) @@ -428,7 +399,7 @@ public override bool TransactionInProgress /// Get the current Hibernate transaction. public ITransaction Transaction { - get { return connectionManager.Transaction; } + get { return ConnectionManager.Transaction; } } public override CacheMode CacheMode @@ -472,10 +443,10 @@ public void ManagedClose() { throw new SessionException("Session was already closed!"); } - if (_transactionCoordinatorShared) - connectionManager.RemoveDependentSession(this); + if (TransactionCoordinatorShared) + ConnectionManager.RemoveDependentSession(this); else - connectionManager.Close(); + ConnectionManager.Close(); SetClosed(); } } @@ -800,45 +771,6 @@ public IQueryOver QueryOver(Expression> alias) where T : class } } - /// - /// Begin a NHibernate transaction - /// - /// A NHibernate transaction - public ITransaction BeginTransaction() - { - using (BeginProcess()) - { - if (_transactionCoordinatorShared) - { - // Todo : should seriously consider not allowing a txn to begin from a child session - // can always route the request to the root session... - log.Warn("Transaction started on non-root session"); - } - - return connectionManager.BeginTransaction(); - } - } - - /// - /// Begin a NHibernate transaction with the specified isolation level - /// - /// The isolation level - /// A NHibernate transaction - public ITransaction BeginTransaction(IsolationLevel isolationLevel) - { - using (BeginProcess()) - { - if (_transactionCoordinatorShared) - { - // Todo : should seriously consider not allowing a txn to begin from a child session - // can always route the request to the root session... - log.Warn("Transaction started on non-root session"); - } - - return connectionManager.BeginTransaction(isolationLevel); - } - } - #endregion #region IDisposable Members From 51b6ee8dec1b18c4295b38c4ae5fbdb5b0d62503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Mon, 22 Jan 2018 17:01:21 +0100 Subject: [PATCH 3/8] Additional move of duplicated code. --- src/NHibernate/Impl/AbstractSessionImpl.cs | 8 ++++++++ src/NHibernate/Impl/SessionImpl.cs | 5 +---- src/NHibernate/Impl/StatelessSessionImpl.cs | 5 +---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/NHibernate/Impl/AbstractSessionImpl.cs b/src/NHibernate/Impl/AbstractSessionImpl.cs index ebe2f5d233b..dabde20450b 100644 --- a/src/NHibernate/Impl/AbstractSessionImpl.cs +++ b/src/NHibernate/Impl/AbstractSessionImpl.cs @@ -407,6 +407,14 @@ protected internal void SetClosed() closed = true; } + protected DbConnection CloseConnectionManager() + { + if (!TransactionCoordinatorShared) + return ConnectionManager.Close(); + ConnectionManager.RemoveDependentSession(this); + return null; + } + private void InitQuery(IQuery query, NamedQueryDefinition nqd) { query.SetCacheable(nqd.IsCacheable); diff --git a/src/NHibernate/Impl/SessionImpl.cs b/src/NHibernate/Impl/SessionImpl.cs index f2c34e43e1b..27f69a0f986 100644 --- a/src/NHibernate/Impl/SessionImpl.cs +++ b/src/NHibernate/Impl/SessionImpl.cs @@ -278,10 +278,7 @@ public DbConnection Close() try { - if (!TransactionCoordinatorShared) - return ConnectionManager.Close(); - ConnectionManager.RemoveDependentSession(this); - return null; + return CloseConnectionManager(); } finally { diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 2434f2055c9..f17ac4fe9f9 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -443,10 +443,7 @@ public void ManagedClose() { throw new SessionException("Session was already closed!"); } - if (TransactionCoordinatorShared) - ConnectionManager.RemoveDependentSession(this); - else - ConnectionManager.Close(); + CloseConnectionManager(); SetClosed(); } } From e11c333b7b603a997edb4e6855ea6a664ab8945e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Mon, 22 Jan 2018 17:18:19 +0100 Subject: [PATCH 4/8] Move in common overrides duplicated since connection manager own move. --- .../Async/Impl/StatelessSessionImpl.cs | 1 - src/NHibernate/Engine/ISessionImplementor.cs | 10 +++++- src/NHibernate/ISession.cs | 10 +++++- src/NHibernate/IStatelessSession.cs | 10 +++++- src/NHibernate/Impl/AbstractSessionImpl.cs | 20 +++++++++--- src/NHibernate/Impl/SessionImpl.cs | 31 ------------------- src/NHibernate/Impl/StatelessSessionImpl.cs | 20 ------------ 7 files changed, 43 insertions(+), 59 deletions(-) diff --git a/src/NHibernate/Async/Impl/StatelessSessionImpl.cs b/src/NHibernate/Async/Impl/StatelessSessionImpl.cs index 70a8c800dcf..ce0fe896e0a 100644 --- a/src/NHibernate/Async/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Async/Impl/StatelessSessionImpl.cs @@ -11,7 +11,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Data.Common; using System.Linq.Expressions; using NHibernate.Cache; using NHibernate.Collection; diff --git a/src/NHibernate/Engine/ISessionImplementor.cs b/src/NHibernate/Engine/ISessionImplementor.cs index f32306fde2d..772d7691249 100644 --- a/src/NHibernate/Engine/ISessionImplementor.cs +++ b/src/NHibernate/Engine/ISessionImplementor.cs @@ -274,8 +274,16 @@ public partial interface ISessionImplementor bool IsOpen { get; } /// - /// Is the ISession currently connected? + /// Is the session connected? /// + /// + /// if the session is connected. + /// + /// + /// A session is considered connected if there is a (regardless + /// of its state) or if the field connect is true. Meaning that it will connect + /// at the next operation that requires a connection. + /// bool IsConnected { get; } FlushMode FlushMode { get; set; } diff --git a/src/NHibernate/ISession.cs b/src/NHibernate/ISession.cs index 9306c62332e..71d09cb207c 100644 --- a/src/NHibernate/ISession.cs +++ b/src/NHibernate/ISession.cs @@ -191,8 +191,16 @@ public partial interface ISession : IDisposable bool IsOpen { get; } /// - /// Is the ISession currently connected? + /// Is the session connected? /// + /// + /// if the session is connected. + /// + /// + /// A session is considered connected if there is a (regardless + /// of its state) or if the field connect is true. Meaning that it will connect + /// at the next operation that requires a connection. + /// bool IsConnected { get; } /// diff --git a/src/NHibernate/IStatelessSession.cs b/src/NHibernate/IStatelessSession.cs index c5291a3e13b..495c8703b81 100644 --- a/src/NHibernate/IStatelessSession.cs +++ b/src/NHibernate/IStatelessSession.cs @@ -45,8 +45,16 @@ public partial interface IStatelessSession : IDisposable bool IsOpen { get; } /// - /// Is the IStatelessSession currently connected? + /// Is the session connected? /// + /// + /// if the session is connected. + /// + /// + /// A session is considered connected if there is a (regardless + /// of its state) or if the field connect is true. Meaning that it will connect + /// at the next operation that requires a connection. + /// bool IsConnected { get; } /// diff --git a/src/NHibernate/Impl/AbstractSessionImpl.cs b/src/NHibernate/Impl/AbstractSessionImpl.cs index dabde20450b..030f145b66b 100644 --- a/src/NHibernate/Impl/AbstractSessionImpl.cs +++ b/src/NHibernate/Impl/AbstractSessionImpl.cs @@ -108,7 +108,16 @@ public ISessionFactoryImplementor Factory protected set => _factory = value; } - public abstract IBatcher Batcher { get; } + // 6.0 TODO: remove virtual. + /// + public virtual IBatcher Batcher + { + get + { + CheckAndUpdateSessionStatus(); + return ConnectionManager.Batcher; + } + } public abstract void CloseSessionFromSystemTransaction(); public virtual IList List(IQueryExpression queryExpression, QueryParameters parameters) @@ -244,8 +253,13 @@ public virtual IQuery GetNamedSQLQuery(string name) } } - // 6.0 TODO: remove virtual. + // 6.0 TODO: remove virtual from below properties. + /// public virtual ConnectionManager ConnectionManager { get; protected set; } + /// + public virtual bool IsConnected => ConnectionManager.IsConnected; + /// + public virtual DbConnection Connection => ConnectionManager.GetConnection(); public abstract IQueryTranslator[] GetQueries(IQueryExpression query, bool scalar); public abstract EventListeners Listeners { get; } @@ -254,11 +268,9 @@ public virtual IQuery GetNamedSQLQuery(string name) public abstract IPersistenceContext PersistenceContext { get; } public abstract CacheMode CacheMode { get; set; } public abstract bool IsOpen { get; } - public abstract bool IsConnected { get; } public abstract string FetchProfile { get; set; } public abstract string BestGuessEntityName(object entity); public abstract string GuessEntityName(object entity); - public abstract DbConnection Connection { get; } public abstract int ExecuteNativeUpdate(NativeSQLQuerySpecification specification, QueryParameters queryParameters); public abstract FutureCriteriaBatch FutureCriteriaBatch { get; protected internal set; } public abstract FutureQueryBatch FutureQueryBatch { get; protected internal set; } diff --git a/src/NHibernate/Impl/SessionImpl.cs b/src/NHibernate/Impl/SessionImpl.cs index 27f69a0f986..c507823902e 100644 --- a/src/NHibernate/Impl/SessionImpl.cs +++ b/src/NHibernate/Impl/SessionImpl.cs @@ -229,16 +229,6 @@ protected internal set } } - /// - public override IBatcher Batcher - { - get - { - CheckAndUpdateSessionStatus(); - return ConnectionManager.Batcher; - } - } - public ConnectionReleaseMode ConnectionReleaseMode { get { return connectionReleaseMode; } @@ -1452,27 +1442,6 @@ public override void InitializeCollection(IPersistentCollection collection, bool } } - public override DbConnection Connection - { - get { return ConnectionManager.GetConnection(); } - } - - /// - /// Gets if the ISession is connected. - /// - /// - /// if the ISession is connected. - /// - /// - /// An ISession is considered connected if there is an (regardless - /// of its state) or if it the field connect is true. Meaning that it will connect - /// at the next operation that requires a connection. - /// - public override bool IsConnected - { - get { return ConnectionManager.IsConnected; } - } - /// public DbConnection Disconnect() { diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index f17ac4fe9f9..f04fa45caf2 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Data.Common; using System.Linq.Expressions; using NHibernate.Cache; using NHibernate.Collection; @@ -88,15 +87,6 @@ public override long Timestamp get { throw new NotSupportedException(); } } - public override IBatcher Batcher - { - get - { - CheckAndUpdateSessionStatus(); - return ConnectionManager.Batcher; - } - } - public override void CloseSessionFromSystemTransaction() { Dispose(true); @@ -333,11 +323,6 @@ public override bool IsOpen get { return !IsClosed; } } - public override bool IsConnected - { - get { return ConnectionManager.IsConnected; } - } - public override FlushMode FlushMode { get { return FlushMode.Commit; } @@ -365,11 +350,6 @@ public override string GuessEntityName(object entity) } } - public override DbConnection Connection - { - get { return ConnectionManager.GetConnection(); } - } - public IStatelessSession SetBatchSize(int batchSize) { Batcher.BatchSize = batchSize; From 3e6da46d3f3ee86423724cd7d01bee23ab41d6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Mon, 22 Jan 2018 17:22:26 +0100 Subject: [PATCH 5/8] Move in common a property duplicated since connection manager own move. --- src/NHibernate/Impl/AbstractSessionImpl.cs | 3 +++ src/NHibernate/Impl/SessionImpl.cs | 5 ----- src/NHibernate/Impl/StatelessSessionImpl.cs | 6 ------ 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/NHibernate/Impl/AbstractSessionImpl.cs b/src/NHibernate/Impl/AbstractSessionImpl.cs index 030f145b66b..fbc8f98b90b 100644 --- a/src/NHibernate/Impl/AbstractSessionImpl.cs +++ b/src/NHibernate/Impl/AbstractSessionImpl.cs @@ -32,6 +32,9 @@ public abstract partial class AbstractSessionImpl : ISessionImplementor private bool closed; + /// Get the current NHibernate transaction. + public ITransaction Transaction => ConnectionManager.Transaction; + protected bool TransactionCoordinatorShared { get; } public ITransactionContext TransactionContext diff --git a/src/NHibernate/Impl/SessionImpl.cs b/src/NHibernate/Impl/SessionImpl.cs index c507823902e..b2d90222951 100644 --- a/src/NHibernate/Impl/SessionImpl.cs +++ b/src/NHibernate/Impl/SessionImpl.cs @@ -1286,11 +1286,6 @@ public void Refresh(object obj, LockMode lockMode) } } - public ITransaction Transaction - { - get { return ConnectionManager.Transaction; } - } - /// /// /// diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index f04fa45caf2..67445b23847 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -376,12 +376,6 @@ public override bool TransactionInProgress #region IStatelessSession Members - /// Get the current Hibernate transaction. - public ITransaction Transaction - { - get { return ConnectionManager.Transaction; } - } - public override CacheMode CacheMode { get { return CacheMode.Ignore; } From 683f3af7acabdf8bea4485edb2e6bad9048642ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Mon, 22 Jan 2018 17:30:39 +0100 Subject: [PATCH 6/8] Move in common a property with an undue discrepancy. This is not fixing internal troubles since this property has no usages. --- src/NHibernate/Impl/AbstractSessionImpl.cs | 4 +++- src/NHibernate/Impl/SessionImpl.cs | 5 ----- src/NHibernate/Impl/StatelessSessionImpl.cs | 5 ----- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/NHibernate/Impl/AbstractSessionImpl.cs b/src/NHibernate/Impl/AbstractSessionImpl.cs index fbc8f98b90b..84da62c3385 100644 --- a/src/NHibernate/Impl/AbstractSessionImpl.cs +++ b/src/NHibernate/Impl/AbstractSessionImpl.cs @@ -413,7 +413,9 @@ protected bool IsAlreadyDisposed public abstract void Flush(); - public abstract bool TransactionInProgress { get; } + // 6.0 TODO: remove virtual. + /// + public virtual bool TransactionInProgress => ConnectionManager.IsInActiveTransaction; #endregion diff --git a/src/NHibernate/Impl/SessionImpl.cs b/src/NHibernate/Impl/SessionImpl.cs index b2d90222951..13529e989b5 100644 --- a/src/NHibernate/Impl/SessionImpl.cs +++ b/src/NHibernate/Impl/SessionImpl.cs @@ -1326,11 +1326,6 @@ public override void Flush() } } - public override bool TransactionInProgress - { - get { return ConnectionManager.IsInActiveTransaction; } - } - public bool IsDirty() { using (BeginProcess()) diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 67445b23847..6cb435cfc6c 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -369,11 +369,6 @@ public void ManagedFlush() } } - public override bool TransactionInProgress - { - get { return Transaction.IsActive; } - } - #region IStatelessSession Members public override CacheMode CacheMode From cc2d43190d497ba9f3c47bd55739408dd794dab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Mon, 22 Jan 2018 17:35:27 +0100 Subject: [PATCH 7/8] Trivial additional clean-up. Just to get rid of the warning sign (at least with my current settings...). --- src/NHibernate/Impl/StatelessSessionImpl.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 6cb435cfc6c..1775af7f0c4 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -851,14 +851,14 @@ public override int ExecuteUpdate(IQueryExpression queryExpression, QueryParamet public override FutureCriteriaBatch FutureCriteriaBatch { - get { throw new System.NotSupportedException("future queries are not supported for stateless session"); } - protected internal set { throw new System.NotSupportedException("future queries are not supported for stateless session"); } + get { throw new NotSupportedException("future queries are not supported for stateless session"); } + protected internal set { throw new NotSupportedException("future queries are not supported for stateless session"); } } public override FutureQueryBatch FutureQueryBatch { - get { throw new System.NotSupportedException("future queries are not supported for stateless session"); } - protected internal set { throw new System.NotSupportedException("future queries are not supported for stateless session"); } + get { throw new NotSupportedException("future queries are not supported for stateless session"); } + protected internal set { throw new NotSupportedException("future queries are not supported for stateless session"); } } public override IEntityPersister GetEntityPersister(string entityName, object obj) From 30d8fe2aaac0b6d7550f8db4d12956beae2f2e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Tue, 23 Jan 2018 09:38:52 +0100 Subject: [PATCH 8/8] Rename a property for consistency. --- src/NHibernate/Impl/AbstractSessionImpl.cs | 10 +++++----- src/NHibernate/Impl/SessionImpl.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NHibernate/Impl/AbstractSessionImpl.cs b/src/NHibernate/Impl/AbstractSessionImpl.cs index 84da62c3385..64382f60c32 100644 --- a/src/NHibernate/Impl/AbstractSessionImpl.cs +++ b/src/NHibernate/Impl/AbstractSessionImpl.cs @@ -35,7 +35,7 @@ public abstract partial class AbstractSessionImpl : ISessionImplementor /// Get the current NHibernate transaction. public ITransaction Transaction => ConnectionManager.Transaction; - protected bool TransactionCoordinatorShared { get; } + protected bool IsTransactionCoordinatorShared { get; } public ITransactionContext TransactionContext { @@ -63,7 +63,7 @@ protected internal AbstractSessionImpl(ISessionFactoryImplementor factory, ISess if (options is ISharedSessionCreationOptions sharedOptions && sharedOptions.IsTransactionCoordinatorShared) { // NH specific implementation: need to port Hibernate transaction management. - TransactionCoordinatorShared = true; + IsTransactionCoordinatorShared = true; if (options.UserSuppliedConnection != null) throw new SessionException("Cannot simultaneously share transaction context and specify connection"); var connectionManager = sharedOptions.ConnectionManager; @@ -426,7 +426,7 @@ protected internal void SetClosed() protected DbConnection CloseConnectionManager() { - if (!TransactionCoordinatorShared) + if (!IsTransactionCoordinatorShared) return ConnectionManager.Close(); ConnectionManager.RemoveDependentSession(this); return null; @@ -532,7 +532,7 @@ public ITransaction BeginTransaction() { using (BeginProcess()) { - if (TransactionCoordinatorShared) + if (IsTransactionCoordinatorShared) { // Todo : should seriously consider not allowing a txn to begin from a child session // can always route the request to the root session... @@ -552,7 +552,7 @@ public ITransaction BeginTransaction(IsolationLevel isolationLevel) { using (BeginProcess()) { - if (TransactionCoordinatorShared) + if (IsTransactionCoordinatorShared) { // Todo : should seriously consider not allowing a txn to begin from a child session // can always route the request to the root session... diff --git a/src/NHibernate/Impl/SessionImpl.cs b/src/NHibernate/Impl/SessionImpl.cs index 13529e989b5..cb86f7c0fd2 100644 --- a/src/NHibernate/Impl/SessionImpl.cs +++ b/src/NHibernate/Impl/SessionImpl.cs @@ -121,7 +121,7 @@ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext contex { throw new InvalidOperationException("Cannot serialize a Session while connected"); } - if (TransactionCoordinatorShared) + if (IsTransactionCoordinatorShared) { throw new InvalidOperationException("Cannot serialize a Session sharing its transaction coordinator"); }