【问题标题】:NHibernate multi-threading issueNHibernate 多线程问题
【发布时间】:2011-10-25 07:55:55
【问题描述】:

我有一个 ASP.NET MVC 应用程序,我想为它编写一些压力测试,以了解来自多个线程的并发数据库访问。我使用 Parallel.ForEach() 将其编写为单元测试,但由于大部分时间都出现以下异常,因此无法使其工作:

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

所以我尽可能地简化了测试,就在这里

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

典型输出:

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 看起来像这样:

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

显然我对这个并发访问做错了,但我无法发现错误。谢谢你的建议。

【问题讨论】:

标签: nhibernate


【解决方案1】:

HybridSessionBuilder 将 ISession 存储在静态成员中,因此可以为每个线程重用它。修复测试的最简单解决方案是从 _currentSession 中删除 static 关键字。然后HybridSessionBuilder 的每个实例将提供不同的ISession

【讨论】:

  • 谢谢,我注意到在我发送问题几秒钟后 :) 我为_currentSession 使用了[ThreadStatic] 属性,它似乎运行良好。
猜你喜欢
  • 2011-11-13
  • 2020-01-14
  • 1970-01-01
  • 2011-12-06
  • 2013-06-02
  • 1970-01-01
相关资源
最近更新 更多