使用SQLite:memory:NHibernate时出现问题
本文关键字:问题 NHibernate SQLite memory 使用 | 更新日期: 2023-09-27 17:48:52
我使用NHibernate进行数据访问,有一段时间我没有使用SQLite进行本地集成测试。我一直在使用一个文件,但我想我会去掉:memory:选项。当我启动任何集成测试时,数据库似乎已经创建(NHibernate吐出表创建sql),但与数据库的交互会导致错误。
有人让NHibernate使用内存数据库吗?这可能吗?我使用的连接字符串是:
Data Source=:memory:;Version=3;New=True
SQLite内存数据库只有在与它的连接保持打开的情况下才存在。要在NHibernate的单元测试中使用它:
1.在测试开始时打开一个ISession(可能使用[SetUp]方法)
2.在SchemaExport调用中使用来自该会话的连接
3.在测试中使用相同的会话
4.在测试结束时关闭会话(可能采用[TearDown]方法)。
我能够使用SQLite内存中数据库,并通过使用SQLite对"共享缓存"的支持来避免为每次测试重新构建架构,该支持允许跨连接共享内存中数据库。
我在AssemblyInitialize中执行了以下操作(我使用的是MSTest):
-
将NHibernate(Fluently)配置为使用带有以下连接字符串的SQLite:
FullUri=file:memorydb.db?mode=memory&cache=shared
-
使用该配置创建hbm2ddlSchemaExport对象,并在单独的连接上执行它(但再次使用相同的连接字符串)。
- 保持该连接处于打开状态,并由静态字段引用,直到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;
}
}
}
以及每个单元测试类/固定装置的基类:
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();
}
}
我承认,资源管理可以改进,但这些毕竟是单元测试(欢迎提出改进建议!)。
我们在内存中使用SQLite进行所有数据库测试。我们使用单个ADO连接进行测试,该连接可重复用于同一测试打开的所有NH会话。
- 每次测试前:创建连接
- 在此连接上创建架构
- 运行测试。所有会话都使用相同的连接
- 测试后:闭合连接
这还允许运行包含多个会话的测试。SessionFactory也是为所有测试创建一次的,因为读取映射文件需要相当长的时间。
编辑
共享缓存的使用
自System.Data.Sqlite 1.0.82(或Sqlite 3.7.13)以来,存在一个共享缓存,它允许多个连接共享相同的数据,也适用于内存中数据库。这允许在一个连接中创建内存中的数据库,并在另一个连接中将其使用。(我还没有尝试,但理论上,这应该有效):
- 将连接字符串更改为
file::memory:?cache=shared
- 打开连接并创建架构
- 保持此连接打开,直到测试结束
- 让NH在测试期间创建其他连接(正常行为)
我遇到了类似的问题,即使在如上所述打开ISession并将"Pooling=True;最大池大小=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>
希望它能有所帮助!
我解决了SQLite内存数据库的许多问题。所以现在我们使用SQLite处理ramdrive磁盘上的文件。
这只是一个猜测,但NHibernate使用的sql输出是sqlite不支持的命令吗?
另外,如果使用文件而不是内存,会发生什么情况?(我认为System.IO.Path.GetTempFileName()会起作用…)
我是用Rhino Commons来做的。如果你不想使用Rhino Commons,你可以研究源代码,看看它是如何做到的。我遇到的唯一问题是SQLite不支持嵌套事务。这迫使我更改代码以支持集成测试。与内存中数据库的集成测试非常棒,我认为这是一个公平的折衷方案。
我只想感谢decates。我已经尝试解决这个问题几个月了,我所要做的就是添加
FullUri=file:memorydb.db?mode=memory&cache=shared
到我的nhibernate配置文件中的连接字符串。此外,只使用带有*.hbm.xml的NHibernate,而不使用FNH,根本不需要修改我的代码!
当我忘记导入SQLite Nuget包时,我也遇到了同样的错误。