Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions doc/reference/modules/architecture.xml
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,21 @@
</para>

<itemizedlist>
<listitem>
<para>
<literal>NHibernate.Context.AsyncLocalSessionContext</literal> - current sessions are tracked
by current asynchronous flow. You are responsible to bind and unbind an
<literal>ISession</literal> instance with static methods of class
<literal>CurrentSessionContext</literal>. Binding operations from inner flows will not be
propagated to outer or siblings flows.
</para>
</listitem>
<listitem>
<para>
<literal>NHibernate.Context.CallSessionContext</literal> - current sessions are tracked
by <literal>CallContext</literal>. You are responsible to bind and unbind an <literal>
ISession</literal> instance with static methods of class <literal>CurrentSessionContext
</literal>.
by <literal>CallContext</literal>. You are responsible to bind and unbind an
<literal>ISession</literal> instance with static methods of class
<literal>CurrentSessionContext</literal>.
</para>
</listitem>
<listitem>
Expand All @@ -257,26 +266,26 @@
</listitem>
<listitem>
<para>
<literal>NHibernate.Context.WebSessionContext</literal> -
stores the current session in <literal>HttpContext</literal>.
You are responsible to bind and unbind an <literal>ISession</literal>
instance with static methods of class <literal>CurrentSessionContext</literal>.
<literal>NHibernate.Context.WebSessionContext</literal> -
stores the current session in <literal>HttpContext</literal>.
You are responsible to bind and unbind an <literal>ISession</literal>
instance with static methods of class <literal>CurrentSessionContext</literal>.
</para>
</listitem>
<listitem>
<para>
<literal>NHibernate.Context.WcfOperationSessionContext</literal> - current sessions are tracked
by WCF <literal>OperationContext</literal>. You need to register the <literal>WcfStateExtension</literal>
extension in WCF. You are responsible to bind and unbind an <literal>ISession
</literal> instance with static methods of class <literal>CurrentSessionContext</literal>.
extension in WCF. You are responsible to bind and unbind an <literal>ISession</literal>
instance with static methods of class <literal>CurrentSessionContext</literal>.
</para>
</listitem>
<listitem>
<para>
<literal>NHibernate.Context.ManagedWebSessionContext</literal> - current
sessions are tracked by <literal>HttpContext</literal>. Removed in NHibernate 4.0
- <literal>NHibernate.Context.WebSessionContext</literal> should be used instead.
You are responsible to bind and unbind an <literal>ISession</literal> instance with static methods
sessions are tracked by <literal>HttpContext</literal>. Removed in NHibernate 4.0
- <literal>NHibernate.Context.WebSessionContext</literal> should be used instead.
You are responsible to bind and unbind an <literal>ISession</literal> instance with static methods
on this class, it never opens, flushes, or closes an <literal>ISession</literal> itself.
</para>
</listitem>
Expand All @@ -287,7 +296,8 @@
defines which <literal>NHibernate.Context.ICurrentSessionContext</literal> implementation
should be used. Typically, the value of this parameter would just name the implementation
class to use (including the assembly name); for the out-of-the-box implementations, however,
there are corresponding short names: "call", "thread_static", "web" and "wcf_operation",
there are corresponding short names: <literal>async_local</literal>, <literal>call</literal>,
<literal>thread_static</literal>, <literal>web</literal> and <literal>wcf_operation</literal>,
respectively.
</para>
</sect1>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Threading.Tasks;
using NHibernate.Cfg;
using NHibernate.Context;
using NUnit.Framework;

namespace NHibernate.Test.ConnectionTest
{
[TestFixture]
public class AsyncLocalSessionContextFixture : ConnectionManagementTestCase
{
protected override ISession GetSessionUnderTest()
{
var session = OpenSession();
session.BeginTransaction();
return session;
}

protected override void Configure(Configuration configuration)
{
base.Configure(cfg);
cfg.SetProperty(Environment.CurrentSessionContextClass, "async_local");
}

[Test]
public async Task AsyncLocalIsolation()
{
using (var session = OpenSession())
{
CurrentSessionContext.Bind(session);
AssertCurrentSession(session, "Unexpected session after outer bind.");

await SubBind(session);
AssertCurrentSession(session, "Unexpected session after end of SubBind call.");
}
}

private async Task SubBind(ISession firstSession)
{
AssertCurrentSession(firstSession, "Unexpected session at start of SubBind.");

using (var session = OpenSession())
{
CurrentSessionContext.Bind(session);
AssertCurrentSession(session, "Unexpected session after inner bind.");

await Dummy();
AssertCurrentSession(session, "Unexpected session after dummy await.");
}
}

private Task Dummy()
{
return Task.FromResult(0);
}

private void AssertCurrentSession(ISession session, string message)
{
Assert.That(
Sfi.GetCurrentSession(),
Is.EqualTo(session),
"{0} {1} instead of {2}.", message,
Sfi.GetCurrentSession().GetSessionImplementation().SessionId,
session.GetSessionImplementation().SessionId);
}
}
}
1 change: 1 addition & 0 deletions src/NHibernate.Test/NHibernate.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@
<Compile Include="ConnectionTest\BatcherFixture.cs" />
<Compile Include="ConnectionTest\AggressiveReleaseTest.cs" />
<Compile Include="ConnectionTest\ConnectionManagementTestCase.cs" />
<Compile Include="ConnectionTest\AsyncLocalSessionContextFixture.cs" />
<Compile Include="ConnectionTest\YetAnother.cs" />
<Compile Include="ConnectionTest\Other.cs" />
<Compile Include="ConnectionTest\Silly.cs" />
Expand Down
33 changes: 33 additions & 0 deletions src/NHibernate/Context/AsyncLocalSessionContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Threading;
using NHibernate.Engine;

namespace NHibernate.Context
{
// Session contextes are serializable while not actually serializing any data. Others contextes just retrieve it back
// from their context, if it does still live when/where they are de-serialized. For having that with AsyncLocal,
// we would need to store it as static, and then we need to use a MapBasedSessionContext.
// But this would cause bindings operations done in inner flows to be potentially propagated to outer flows, depending
// on which flow has initialized the map. This is undesirable.
// So current implementation just loses its context in case of serialization, since AsyncLocal is not serializable.
// Another option would be to change MapBasedSessionContext for recreating a new dictionary from the
// previous one at each change, essentially using those dictionaries as immutable objects.
/// <summary>
/// Provides a <see cref="ISessionFactory.GetCurrentSession()">current session</see>
/// for current asynchronous flow.
/// </summary>
[Serializable]
public class AsyncLocalSessionContext : CurrentSessionContext
{
private readonly AsyncLocal<ISession> _session = new AsyncLocal<ISession>();

// Constructor signature required for dynamic invocation code.
public AsyncLocalSessionContext(ISessionFactoryImplementor factory) { }

protected override ISession Session
{
get => _session.Value;
set => _session.Value = value;
}
}
}
2 changes: 2 additions & 0 deletions src/NHibernate/Impl/SessionFactoryImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,8 @@ private ICurrentSessionContext BuildCurrentSessionContext()
{
case null:
return null;
case "async_local":
return new AsyncLocalSessionContext(this);
case "call":
return new CallSessionContext(this);
case "thread_static":
Expand Down
1 change: 1 addition & 0 deletions src/NHibernate/NHibernate.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
<Compile Include="Connection\DriverConnectionProvider.cs" />
<Compile Include="Connection\IConnectionProvider.cs" />
<Compile Include="Connection\UserSuppliedConnectionProvider.cs" />
<Compile Include="Context\AsyncLocalSessionContext.cs" />
<Compile Include="Dialect\PostgreSQL83Dialect.cs" />
<Compile Include="Impl\ISharedSessionCreationOptions.cs" />
<Compile Include="ISharedSessionBuilder.cs" />
Expand Down