NHibernate-multi-threading-Problem
Habe ich eine ASP.NET MVC-Anwendung, für die ich schreiben will stress-tests im Sinne der gleichzeitigen Datenbank-Zugriff von mehreren threads. Ich schrieb es als ein unit-test mit Parallel.ForEach(), aber war nicht in der Lage zu machen, arbeiten, da war ich immer folgende exception die meiste Zeit:
There is already an open DataReader associated with this Command which must be closed first
Also ich vereinfacht das testen so viel wie möglich, und hier ist es
[Test]
public void Can_Access_DB_Concurrently()
{
Parallel.ForEach(Enumerable.Range(0, 9), x =>
{
try
{
var sessionBuilder = new HybridSessionBuilder();
var session = sessionBuilder.GetSession();
using (ITransaction transaction = session.BeginTransaction())
{
var job = session.Query<Job>().Where(y => y.Name == "TestProject").SingleOrDefault().Name;
Trace.WriteLine(Thread.CurrentThread.ManagedThreadId + ": Job name is " + job);
transaction.Commit();
}
}
catch (Exception e)
{
Trace.WriteLine(Thread.CurrentThread.ManagedThreadId + ": Exception: " + e.Message);
}
});
}
Typische Ausgabe:
13: Exception: Object reference not set to an instance of an object.
16: Exception: There is already an open DataReader associated with this Command which must be closed first.
9: Exception: There is already an open DataReader associated with this Command which must be closed first.
16: Exception: There is already an open DataReader associated with this Command which must be closed first.
14: Exception: There is already an open DataReader associated with this Command which must be closed first.
Den HybridSessionBuilder
sieht wie folgt aus:
public class HybridSessionBuilder : ISessionBuilder
{
private static ISessionFactory _sessionFactory;
private static ISession _currentSession;
public ISession GetSession()
{
ISessionFactory factory = getSessionFactory();
ISession session = getExistingOrNewSession(factory);
return session;
}
private ISessionFactory getSessionFactory()
{
lock (this)
{
if (_sessionFactory == null)
{
Configuration configuration = GetConfiguration();
_sessionFactory = configuration.BuildSessionFactory();
}
return _sessionFactory;
}
}
public Configuration GetConfiguration()
{
string connectionString = WebConfigurationManager.ConnectionStrings["StagingDatabase"].ConnectionString;
Configuration configuration = new Configuration();
configuration = PostgreSQLConfiguration.PostgreSQL82
.ConnectionString(connectionString)
.Dialect("NHibernate.Dialect.PostgreSQL82Dialect")
.UseReflectionOptimizer()
.AdoNetBatchSize(50)
.ConfigureProperties(new Configuration());
configuration.AddMappingsFromAssembly(typeof(Job).Assembly);
Fluently.Configure(configuration);
return configuration;
}
private ISession getExistingOrNewSession(ISessionFactory factory)
{
{
if (HttpContext.Current != null)
{
ISession session = GetExistingWebSession();
if (session == null)
{
session = openSessionAndAddToContext(factory);
}
else if (!session.IsOpen)
{
session = openSessionAndAddToContext(factory);
}
return session;
}
if (_currentSession == null)
{
_currentSession = factory.OpenSession();
}
else if (!_currentSession.IsOpen)
{
_currentSession = factory.OpenSession();
}
}
return _currentSession;
}
public ISession GetExistingWebSession()
{
return HttpContext.Current.Items[GetType().FullName] as ISession;
}
private ISession openSessionAndAddToContext(ISessionFactory factory)
{
ISession session = factory.OpenSession();
HttpContext.Current.Items.Remove(GetType().FullName);
HttpContext.Current.Items.Add(GetType().FullName, session);
return session;
}
}
Bin ich anscheinend etwas falsch mit dieser gleichzeitigen Zugriff, aber ich bin nicht in der Lage zu erkennen den Fehler. Danke für jeden Rat.
- Sitzung in NHibernate sind nicht thread-sicher. Siehe ähnliche Frage hier: stackoverflow.com/questions/242961/...
Du musst angemeldet sein, um einen Kommentar abzugeben.
HybridSessionBuilder
ist die Speicherung der ISession in einen statischen member und daher die Wiederverwendung es für jeden thread. Die einfachste Lösung zum befestigen Ihres tests zu entfernen, diestatic
Schlüsselwort aus_currentSession
. Jede Instanz vonHybridSessionBuilder
würde dann einen anderenISession
.[ThreadStatic]
Attribut für_currentSession
und es scheint gut zu funktionieren.