使用SQLite的问题:内存:使用NHibernate

时间:2008-10-09 21:27:24

标签: c# nhibernate sqlite orm integration-testing

我使用NHibernate作为我的dataacess,并且有一段时间我没有使用SQLite进行本地集成测试。我一直在使用文件,但我想我会把:memory:选项。当我启动任何集成测试时,似乎创建了数据库(NHibernate吐出表创建sql)但是与数据库的交互会导致错误。

有没有人让NHibernate与内存数据库一起工作?它甚至可能吗?我正在使用的连接字符串是:

Data Source=:memory:;Version=3;New=True

9 个答案:

答案 0 :(得分:41)

只要与SQLite内存数据库的连接保持打开状态,它就会存在。在NHibernate的单元测试中使用它:
1.在测试开始时打开一个ISession(可能是[SetUp]方法) 2.在SchemaExport呼叫中使用该会话的连接 3.在测试中使用相同的会话 4.在测试结束时关闭会话(可能在[TearDown]方法中)。

答案 1 :(得分:18)

我能够使用SQLite内存数据库并避免使用SQLite的support for 'Shared Cache'重建每个测试的模式,这允许跨连接共享内存数据库。

我在 AssemblyInitialize 中做了以下操作(我正在使用MSTest):

  • 配置NHibernate(Fluently)以使用带有以下连接字符串的SQLite:

    FullUri=file:memorydb.db?mode=memory&cache=shared
    
  • 使用该配置创建一个hbm2ddl。 SchemaExport 对象,并在单独的连接上执行它(但再次使用相同的连接字符串)。

  • 保持该连接打开,并由静态字段引用,直到 AssemblyCleanup ,此时它将被关闭并处理掉。这是因为SQLite需要在内存数据库中至少保留一个活动连接才能知道它仍然是必需的并且避免整理。

在每次测试运行之前,会创建一个新会话,并且测试将在最后回滚的事务中运行。

以下是测试程序集级代码的示例:

[TestClass]
public static class SampleAssemblySetup
{
    private const string ConnectionString = "FullUri=file:memorydb.db?mode=memory&cache=shared";
    private static SQLiteConnection _connection;

    [AssemblyInitialize]
    public static void AssemblyInit(TestContext context)
    {
        var configuration = Fluently.Configure()
                                       .Database(SQLiteConfiguration.Standard.ConnectionString(ConnectionString))
                                       .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.Load("MyMappingsAssembly")))
                                       .ExposeConfiguration(x => x.SetProperty("current_session_context_class", "call"))
                                       .BuildConfiguration();

        // Create the schema in the database
        // Because it's an in-memory database, we hold this connection open until all the tests are finished
        var schemaExport = new SchemaExport(configuration);
        _connection = new SQLiteConnection(ConnectionString);
        _connection.Open();
        schemaExport.Execute(false, true, false, _connection, null);
    }

    [AssemblyCleanup]
    public static void AssemblyTearDown()
    {
        if (_connection != null)
        {
            _connection.Dispose();
            _connection = null;
        }
    }
}

每个单元测试类/ fixture的基类:

public class TestBase
{
    [TestInitialize]
    public virtual void Initialize()
    {
        NHibernateBootstrapper.InitializeSession();
        var transaction = SessionFactory.Current.GetCurrentSession().BeginTransaction();
    }

    [TestCleanup]
    public virtual void Cleanup()
    {
        var currentSession = SessionFactory.Current.GetCurrentSession();
        if (currentSession.Transaction != null)
        {
            currentSession.Transaction.Rollback();
            currentSession.Close();
        }

        NHibernateBootstrapper.CleanupSession();
    }
}

我承认,资源管理可以改进,但毕竟这些都是单元测试(欢迎建议改进!)。

答案 2 :(得分:9)

我们在内存中使用SQLite来进行所有数据库测试。我们使用单个ADO连接进行测试,这些测试可以重复用于同一测试打开的所有NH会话。

  1. 每次测试前:创建连接
  2. 在此连接上创建架构
  3. 运行测试。所有会话都使用相同的连接
  4. 测试后:关闭连接
  5. 这也允许运行包含多个会话的测试。 SessionFactory也为所有测试创建一次,因为读取映射文件需要相当长的时间。


    修改

    使用共享缓存

    自System.Data.Sqlite 1.0.82(或Sqlite 3.7.13)以来,有一个Shared Cache,它允许多个连接共享相同的数据,也适用于In-Memory databases。这允许在一个连接中创建内存数据库,并在另一个连接中使用它。 (我还没试过,但从理论上讲,这应该有效):

    • 将连接字符串更改为file::memory:?cache=shared
    • 打开连接并创建架构
    • 保持此连接打开,直到测试结束
    • 让NH在测试期间创建其他连接(正常行为)。

答案 3 :(得分:8)

我有类似的问题,即使在如上所述打开ISession之后,并且在我的连接字符串中添加“Pooling = True; Max Pool Size = 1”。它有所帮助,但我仍然遇到一些情况,在测试期间连接会关闭(通常是在提交事务之后)。

最终对我有用的是在我的SessionFactory配置中将属性“connection.release_mode”设置为“on_close”。

我在app.config文件中的配置现在看起来像这样:

  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <reflection-optimizer use="true" />
    <session-factory>
      <property name="connection.connection_string_name">testSqlLiteDB</property>
      <property name="connection.driver_class">NHibernate.Driver.SQLite20Driver</property>
      <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <property name="connection.release_mode">on_close</property>
      <property name="dialect">NHibernate.Dialect.SQLiteDialect</property>
      <property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
      <property name="query.substitutions">true=1;false=0</property>
    </session-factory>
  </hibernate-configuration>

希望它有所帮助!

答案 4 :(得分:0)

只是猜测,但是NHibernate使用sqlite不支持的命令输出sql?

此外,如果您使用文件而不是内存会发生什么? (System.IO.Path.GetTempFileName()可以工作,我认为...)

答案 5 :(得分:0)

我正在使用Rhino Commons。如果您不想使用Rhino Commons,您可以研究源代码,看看它是如何做到的。我遇到的唯一问题是SQLite不支持嵌套事务。这迫使我改变我的代码以支持集成测试。内存数据库中的集成测试非常棒,我认为这是一个公平的妥协。

答案 6 :(得分:0)

我在SQLite内存数据库方面遇到了很多问题。 所以现在我们正在使用SQLite处理硬盘驱动器上的文件。

答案 7 :(得分:0)

只想感谢决定。我试图解决这个问题几个月了,而我所要做的就是添加

FullUri=file:memorydb.db?mode=memory&cache=shared

到我的nhibernate配置文件中的连接字符串。也只使用带有* .hbm.xml而不是FNH的NHibernate,并且根本不需要修改我的代码!

答案 8 :(得分:0)

当我忘记导入SQLite Nuget包时,我遇到了同样的错误。