Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NH-3402 - Refactor ThreadLocalSessionContext #254

Closed
wants to merge 3 commits into from
Closed
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
159 changes: 8 additions & 151 deletions src/NHibernate/Context/ThreadLocalSessionContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading;

using NHibernate;
using NHibernate.Engine;
Expand All @@ -19,165 +20,21 @@ namespace NHibernate.Context
/// generated here are unusable until after {@link Session#beginTransaction()}
/// has been called. If <tt>Close()</tt> is called on a session managed by
/// this class, it will be automatically unbound.
/// <p/>
/// Additionally, the static <see cref="Bind"/> and <see cref="Unbind"/> methods are
/// provided to allow application code to explicitly control opening and
/// closing of these sessions. This, with some from of interception,
/// is the preferred approach. It also allows easy framework integration
/// and one possible approach for implementing long-sessions.
/// <p/>
/// </summary>
[Serializable]
public class ThreadLocalSessionContext : ICurrentSessionContext
public class ThreadLocalSessionContext : CurrentSessionContext
{
private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(ThreadLocalSessionContext));
private ThreadLocal<ISession> _session = new ThreadLocal<ISession>();

[ThreadStatic]
protected static IDictionary<ISessionFactory, ISession> context;

protected readonly ISessionFactoryImplementor factory;


public ThreadLocalSessionContext(ISessionFactoryImplementor factory)
{
this.factory = factory;
}

#region ICurrentSessionContext Members

public ISession CurrentSession()
{
ISession current = ExistingSession(factory);
if (current == null)
{
current = BuildOrObtainSession();

// wrap the session in the transaction-protection proxy
if (NeedsWrapping(current))
{
current = Wrap(current);
}
// then bind it
DoBind(current, factory);
}
return current;
}

#endregion

private static void CleanupAnyOrphanedSession(ISessionFactory factory)
{
ISession orphan = DoUnbind(factory, false);

if (orphan != null)
{
log.Warn("Already session bound on call to bind(); make sure you clean up your sessions!");

try
{
if (orphan.Transaction != null && orphan.Transaction.IsActive)
{
try
{
orphan.Transaction.Rollback();
}
catch (Exception ex)
{
log.Debug("Unable to rollback transaction for orphaned session", ex);
}
}
orphan.Close();
}
catch (Exception ex)
{
log.Debug("Unable to close orphaned session", ex);
}
}
}

public static void Bind(ISession session)
{
ISessionFactory factory = session.SessionFactory;
CleanupAnyOrphanedSession(factory);
DoBind(session, factory);
}

/// <summary>
/// Unassociate a previously bound session from the current thread of execution.
/// </summary>
/// <param name="factory"></param>
/// <returns></returns>
public static ISession Unbind(ISessionFactory factory)
{
return DoUnbind(factory, true);
}

private static void DoBind(ISession current, ISessionFactory factory)
{
context = context ?? new Dictionary<ISessionFactory, ISession>();

context.Add(factory, current);
}

private static ISession DoUnbind(ISessionFactory factory, bool releaseMapIfEmpty)
{
ISession session = null;

if (context != null)
{
if (context.TryGetValue(factory, out session))
{
context.Remove(factory);
}

if (releaseMapIfEmpty && context.Count == 0)
context = null;
}
return session;
}

private ISession Wrap(ISession current)
{
return current;
}

private bool NeedsWrapping(ISession current)
{
return false;
}

protected ISession BuildOrObtainSession()
{
return factory.OpenSession(
null,
IsAutoFlushEnabled(),
IsAutoCloseEnabled(),
GetConnectionReleaseMode());
}

private ConnectionReleaseMode GetConnectionReleaseMode()
{
return factory.Settings.ConnectionReleaseMode;
}

protected virtual bool IsAutoCloseEnabled()
{
return true;
}

protected virtual bool IsAutoFlushEnabled()
public ThreadLocalSessionContext(Engine.ISessionFactoryImplementor factory)
{
return true;
}

private static ISession ExistingSession(ISessionFactory factory)
/// <summary> Gets or sets the currently bound session. </summary>
protected override ISession Session
{
if (context == null)
return null;

ISession result;
context.TryGetValue(factory, out result);
return result;
get { return _session.Value; }
set { _session.Value = value; }
}
}
}
6 changes: 6 additions & 0 deletions src/NHibernate/Impl/SessionImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using NHibernate.Persister.Collection;
using NHibernate.Persister.Entity;
using NHibernate.Proxy;
using NHibernate.Proxy.DynamicProxy;
using NHibernate.Stat;
using NHibernate.Type;
using NHibernate.Util;
Expand Down Expand Up @@ -1059,6 +1060,11 @@ public override string GuessEntityName(object entity)
{
using (new SessionIdLoggingContext(SessionId))
{
//NH-3448 Fix GuessEntityName
if ((entity is IProxy) && (entity is IFieldInterceptorAccessor))
{
return (entity as IFieldInterceptorAccessor).FieldInterceptor.EntityName;
}
string entityName = interceptor.GetEntityName(entity);
if (entityName == null)
{
Expand Down
2 changes: 1 addition & 1 deletion src/NHibernate/Linq/SqlMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public static class SqlMethods
/// an SQL LIKE expression. (Any 2-argument method named Like in a class named SqlMethods
/// will be translated.)
/// </summary>
public static bool Like(string matchExpression, string pattern)
public static bool Like(this string matchExpression, string pattern)
{
throw new NotSupportedException(
"The NHibernate.Linq.SqlMethods.Like(string, string) method can only be used in Linq2NHibernate expressions.");
Expand Down
6 changes: 6 additions & 0 deletions src/NHibernate/NHibernateUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using NHibernate.Impl;
using NHibernate.Intercept;
using NHibernate.Proxy;
using NHibernate.Proxy.DynamicProxy;
using NHibernate.Type;
using NHibernate.UserTypes;
using NHibernate.Util;
Expand Down Expand Up @@ -407,6 +408,11 @@ public static System.Type GetClass(object proxy)
{
return ((INHibernateProxy)proxy).HibernateLazyInitializer.GetImplementation().GetType();
}
//NH-3448 Fix GuessEntityName
else if ((proxy is IProxy) && (proxy is IFieldInterceptorAccessor))
{
return (proxy as IFieldInterceptorAccessor).FieldInterceptor.MappedClass;
}
else
{
return proxy.GetType();
Expand Down