NHibernate数据在多线程应用程序中不同步

时间:2015-07-18 13:12:12

标签: c# multithreading nhibernate fluent-nhibernate

我在多线程场景中使用NHibernate。这是一项执行以下操作的测试。

  1. (启动多线程服务器。)
  2. 向服务器发送请求。服务器创建一个状态为Foo的新A对象,并使用NHibernate将其存储在数据库中。
  3. 为新创建的B对象设置状态Foo
  4. 向服务器发送请求。服务器使用NHibernate从数据库获取Foo,读取其状态(应该是B)并采取相应的行动。
  5. (停止服务器。)
  6. 现在我的问题是,在步骤4中偶尔(在1 - 2%的情况下)服务器读取状态A时应该读取B并且测试失败。 我在这里做错了什么?

    通过CurrentSessionContext检索NHibernate会话,该会话设置为线程静态。我使用流利的NHibernate,但我想这并不重要。

    修改

    有关服务器的详细信息: 服务器是一个简单的TCP服务器。启动时,它会创建一个新的线程 任务以等待传入连接。当接受新连接时,它会创建另一个线程 任务来处理来自连接的请求并发送响应。处理线程 任务终止。

    服务器使用Task.Factory.StartNew()创建新的线程 任务

    编辑2

    我写了一个小的NUnit测试来重现这种行为。它失败了

      

    预期:开放但是:已初始化

    在第一次迭代。

    [Test]
    [Explicit]
    public void NHibernateProblem()
    {
        for (var i = 0; i < 100; i++)
        {
            var identifier = Guid.NewGuid().ToString();
            var session1 = Database.GetCurrentSession();
            using (var dbTransaction = session1.BeginTransaction())
            {
                var transaction1 = new Transaction
                {
                    Identifier = identifier,
                    Status = TransactionStatus.Initialized
                };
    
                session1.SaveOrUpdate(transaction1);
                dbTransaction.Commit();
            }
    
            Task.Factory.StartNew(() =>
            {
                var session2 = Database.GetCurrentSession();
                using (var dbTransaction = session2.BeginTransaction())
                {
                    var transaction2 = session2
                        .QueryOver<Transaction>()
                        .Where(t => t.Identifier == identifier)
                        .SingleOrDefault();
    
                    transaction2.Status = TransactionStatus.Open;
    
                    session2.SaveOrUpdate(transaction2);
                    dbTransaction.Commit();
                }
            }).Wait();
    
            var session3 = Database.GetCurrentSession();
            using (var dbTransaction = session3.BeginTransaction())
            {
                var transaction3 = session3
                    .QueryOver<Transaction>()
                    .Where(t => t.Identifier == identifier)
                    .SingleOrDefault();
    
                dbTransaction.Commit();
                Console.WriteLine("iteration {0}", i);
    
                Assert.That(transaction3.Status, Is.EqualTo(TransactionStatus.Open));
            }
        }
    }
    

    Database.GetCurrentSession()看起来像这样:

    public virtual ISession GetCurrentSession()
    {
        if (CurrentSessionContext.HasBind(SessionFactory))
        {
            var currentSession = SessionFactory.GetCurrentSession();
            return currentSession;
        }
    
        var session = SessionFactory.OpenSession();
        session.FlushMode = FlushMode.Always;
        session.CacheMode = CacheMode.Ignore;
        CurrentSessionContext.Bind(session);
        return session;
    }
    

    我认为映射的相关性较低。

2 个答案:

答案 0 :(得分:1)

在显示的单元测试中,session3将与session1相同,因此transaction3将是与transaction1相同的对象实例,因此它仍然会显示添加事务对象时的值。

第三部分中的查询将命中数据库,但NHibernate将看到已经加载了已获取的对象并返回该会话已在内存中保存的实例。它通常不会覆盖对象中的值。

答案 1 :(得分:1)

在服务器中,通常的做法是为每个请求创建一个会话,并在请求开始时启动事务。当请求即将完成时,将根据结果提交或回滚事务。

在您的测试中,它实际上并不模拟这种情况,它对请求1和请求3使用相同的会话,在实际情况中它永远不会出现这种情况。

因此,要测试实际场景,您可以为会话3创建新会话。