NHibernate multithreaded issue

I have an ASP.NET MVC application for which I want to write some stress tests in the sense of simultaneously accessing a database from multiple threads. I wrote it as a unit test using Parallel.ForEach (), but could not get it to work, since most of the time I got the following exception:

There is already an open DataReader associated with this Command which must be closed first 

So I simplified the test as much as possible, and here it is

 [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); } }); } 

Typical Output:

 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. 

HybridSessionBuilder as follows:

 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; } } 

Apparently I am doing something wrong with this concurrent access, but I cannot detect the error. Thanks for any advice.

+4
source share
1 answer

HybridSessionBuilder stores the ISession in a static member and therefore reuses it for each thread. The simplest solution to fix your tests would be to remove the static from _currentSession . Each HybridSessionBuilder instance HybridSessionBuilder then provide a different ISession .

+4
source

All Articles