在内存中的SQLite数据库上使用NHibernate测试时出现随机错误 [英] Random error when testing with NHibernate on an in-Memory SQLite db

查看:110
本文介绍了在内存中的SQLite数据库上使用NHibernate测试时出现随机错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个系统,该系统在收到消息后将其入队(写入表),另一个进程将轮询数据库并将其出队以进行处理.在我的自动测试中,我已经在同一过程中合并了这些操作,但是无法(从概念上来说)无法合并这两个操作中的NH会话.

I have a system which after getting a message - enqueues it (write to a table), and another process polls the DB and dequeues it for processing. In my automatic tests I've merged the operations in the same process, but cannot (conceptually) merge the NH sessions from the two operations.

自然地-问题出现了.

我已经阅读了有关使SQLite-InMemory-NHibernate组合在测试环境中工作的一切知识,但是由于没有这样的表"错误,我现在遇到了随机失败的测试.明确地说-随机"表示具有相同确切配置和代码的相同测试有时会失败.

I've read everything I could about getting the SQLite-InMemory-NHibernate combination to work in the testing world, but I've now ran into RANDOMLY failing tests, due to "no such table" errors. To make it clear - "random" means that the same test with the same exact configuration and code will sometimes fail.

我具有以下SQLite配置:

I have the following SQLite configuration:

return SQLiteConfiguration
 .Standard
 .ConnectionString(x => x.Is("Data Source=:memory:; Version=3; New=True; Pooling=True; Max Pool Size=1;"))
 .Raw(NHibernate.Cfg.Environment.ReleaseConnections, "on_close");

在测试(每次测试)开始时,我获取静态"会话提供程序,并请其将现有的数据库清除干净,然后重新创建模式:

At the beginning of my test (every test) I fetch the "static" session provider, and kindly ask it to flush the existing DB clean, and recreate the schema:

public void PurgeDatabaseOrCreateNew()
{
    using (var session = GetNewSession())
    using (var tx = session.BeginTransaction())
    {
            PurgeDatabaseOrCreateNew(session);
            tx.Commit();
    }
}

private void PurgeDatabaseOrCreateNew(ISession session)
{
    //http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx
    new SchemaExport(_Configuration)
        .Execute(false, true, false, session.Connection, null);
}

是的,它在不同的会话上,但是连接是在SQLite上池化的,所以我创建的下一个会话将看到生成的模式.但是,尽管大多数情况下它都起作用,但有时以后的入队"操作将失败,因为它看不到我收到的消息的表. 同样-每次测试套件运行最多可能发生一两次.并非所有测试都失败了,只有第一个失败了(有时还会失败.不确定第二个还是第二个).

So yes, it's on a different session, but the connection is pooled on SQLite, so the next session I create will see the generated schema. Yet, while most of the times it works - sometimes the later "enqueue" operation will fail because it cannot see a table for my incoming messages. Also - that seems to happen at max one or twice per test suite run; not all the tests are failing, just the first one (and sometimes another one. Not quite sure if it's the second or not).

自然,最糟糕的部分是随机性.我告诉自己,我已经修复了好几次,只是因为它只是停止了失败".随机地.

The worst part is the randomness, naturally. I've told myself I've fixed this several times now, just because it simply "stopped failing". At random.

这发生在FW4.0,System.Data.SQLite x86版本,Win7 64b和2008R2(总共三台不同的计算机),在TestDriven.NET 32b进项和NUnit控制台32b进程上配置了FNH的NH2.1.2上.

This happens on FW4.0, System.Data.SQLite x86 version, Win7 64b and 2008R2 (three differen machine in total), NH2.1.2, configured with FNH, on TestDriven.NET 32b precesses and NUnit console 32b processes.

帮助?

推荐答案

我很确定我遇到的问题与您完全相同.每个集成测试我打开和关闭多个会话.在深入研究了SQLite连接池并进行了一些实验之后,我得出以下结论:

Hi I'm pretty sure I have the exact same problem as you. I open and close multiple sessions per integration test. After digging through the SQLite connection pooling and some experimenting of my own, I've come to the following conclusion:

SQLite池代码使用WeakReferences缓存连接,这不是最好的选项进行缓存,因为当没有正常(强)的连接引用且GC运行时,将清除对连接的引用.由于您无法预测GC的运行时间,因此可以解释随机性".尝试在关闭一个会话与打开另一个会话之间添加GC.Collect();,您的测试将始终失败.

The SQLite pooling code caches the connection using WeakReferences, which isn't the best option for caching, since the reference to the connection(s) will be cleared when there is no normal (strong) reference to the connection and the GC runs. Since you can't predict when the GC runs, this explains the "randomness". Try and add a GC.Collect(); between closing one and opening another session, your test will always fail.

我的解决方案是在打开的会话之间自己缓存连接,如下所示:

My solution was to cache the connection myself between opening sessions, like this:

public class BaseIntegrationTest
{
    private static ISessionFactory _sessionFactory;
    private static Configuration _configuration;
    private static SchemaExport _schemaExport;

    // I cache the whole session because I don't want it and the
    // underlying connection to get closed.
    // The "Connection" property of the ISession is what we actually want.
    // Using the NHibernate SQLite Driver to get the connection would probably
    // work too.
    private static ISession _keepConnectionAlive;

    static BaseIntegrationTest()
    {
        _configuration = new Configuration();
        _configuration.Configure();
        _configuration.AddAssembly(typeof(Product).Assembly);
        _sessionFactory = _configuration.BuildSessionFactory();
        _schemaExport = new SchemaExport(_configuration);

        _keepConnectionAlive = _sessionFactory.OpenSession();
    }

    [SetUp]
    protected void RecreateDB()
    {
        _schemaExport.Execute(false, true, false, _keepConnectionAlive.Connection, null);
    }

    protected ISession OpenSession()
    {
        return _sessionFactory.OpenSession(_keepConnectionAlive.Connection);
    }
}

我的每个集成测试都继承自此类,并调用OpenSession()来获取会话.由于[SetUp]属性,NUnit在每次测试之前都会调用RecreateDB.

Each of my integrationtests inherits from this class, and calls OpenSession() to get a session. RecreateDB is called by NUnit before each test because of the [SetUp] attribute.

我希望这对您或其他遇到此错误的人有所帮助.

I hope this helps you or anyone else who gets this error.

这篇关于在内存中的SQLite数据库上使用NHibernate测试时出现随机错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆