使用 SQLite 的 NHibernate 测试 - 使用异步方法时没有这样的 table 错误
NHibernate tests with SQLite - no such table error when using async methods
我在 ASP.NET 6 应用程序中使用 NHibernate。出于集成测试的目的,我正在使用 SQLite 内存数据库。
这是集成测试的 NHibernate 配置的样子:
_configuration = new Configuration();
_configuration.DataBaseIntegration(db =>
{
db.Driver<SQLite20Driver>();
db.Dialect<MySqliteDialect>();
db.ConnectionProvider<SQLiteInMemoryConnectionProvider>();
db.ConnectionString = "Data Source=:memory:;Version=3;New=True;DateTimeKind=Utc;DateTimeFormatString=yyyy-MM-dd HH:mm:ss.FFFFFFF";
db.LogSqlInConsole = true;
db.ConnectionReleaseMode = ConnectionReleaseMode.OnClose;
db.HqlToSqlSubstitutions = "true=1;false=0";
db.SchemaAction = SchemaAutoAction.Validate;
});
var mapping = new ModelMapper();
mapping.AddMappings(typeof(ApplicationUserMapping).Assembly.GetTypes());
// other mappings..
var mappingDocument = mapping.CompileMappingForAllExplicitlyAddedEntities();
_configuration.AddMapping(mappingDocument);
_configuration.LinqToHqlGeneratorsRegistry<DefaultLinqToHqlGeneratorsRegistry>();
var exp = new SchemaExport(_configuration);
exp.Execute(true, true, false);
_sessionFactory = _configuration.BuildSessionFactory();
我有 SettingsService
class 有以下方法:
public async Task<IList<Setting>> GetAll()
{
using var session = _factory.OpenSession();
var settings = await session.QueryOver<Setting>().ListAsync();
return settings;
}
现在,当我从一个简单的 NUnit 测试中调用此方法时:
[Test]
public async Task GetAll()
{
var settings = await new SettingsService(_sessionFactory).GetAll();
}
我遇到一个错误:
NHibernate.Exceptions.GenericADOException : could not execute query
[ SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_ ]
[SQL: SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_]
----> System.Data.SQLite.SQLiteException : SQL logic error
no such table: Settings
整个测试的输出如下所示:
PRAGMA foreign_keys = OFF
drop table if exists Settings
// other tables drops...
PRAGMA foreign_keys = ON
create table Settings (
Id BLOB not null,
Name TEXT not null unique,
Value TEXT not null,
primary key (Id)
)
// other tables creation...
NHibernate: SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_
NHibernate.Exceptions.GenericADOException : could not execute query
[ SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_ ]
[SQL: SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_]
----> System.Data.SQLite.SQLiteException : SQL logic error
no such table: Settings
Data:
actual-sql-query: SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_
at NHibernate.Loader.Loader.DoListAsync(ISessionImplementor session, QueryParameters queryParameters, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder, CancellationToken cancellationToken)
at NHibernate.Loader.Loader.ListIgnoreQueryCacheAsync(ISessionImplementor session, QueryParameters queryParameters, CancellationToken cancellationToken)
at NHibernate.Loader.Criteria.CriteriaLoaderExtensions.LoadAllToListAsync[T](IList`1 loaders, ISessionImplementor session, CancellationToken cancellationToken)
at NHibernate.Impl.SessionImpl.ListAsync[T](CriteriaImpl criteria, CancellationToken cancellationToken)
at NHibernate.Impl.SessionImpl.ListAsync[T](CriteriaImpl criteria, CancellationToken cancellationToken)
因此您可以看到 Settings
table 已创建。
如果我将 GetAll()
方法的实现更改为非异步,即不使用 ListAsync()
,而是使用 List()
函数:
public IList<Setting> GetAll()
{
using var session = _factory.OpenSession();
var settings = session.QueryOver<Setting>().List();
return settings;
}
测试通过(当然是在删除 async
、Task
和 await
之后)。
我见过 this question,但在我的例子中,唯一的区别是使用 NHibernate 的异步方法与非异步方法。我在集成测试的初始化代码和 SettingsService
.
中使用相同的 ISessionFactory
知道这里发生了什么吗?
根据 SQLite docs:
In-Memory database ceases to exist as soon as the database connection
is closed. Every :memory: database is distinct from every other. So,
opening two database connections each with the filename ":memory:"
will create two independent in-memory databases.
默认情况下,每次打开会话时都会创建新连接。所以这个错误是默认设置的预期行为。
但是您使用了一些自定义连接提供程序 SQLiteInMemoryConnectionProvider
,它会重用一旦打开的连接。所以我会说问题出在 SQLiteInMemoryConnectionProvider
内部——它还没有为异步代码做好准备。
确保您的连接提供程序实现了 GetConnection
和 GetConnectionAsync
方法。类似于:
public override DbConnection GetConnection()
{
return _connection ??= base.GetConnection();
}
public override async Task<DbConnection> GetConnectionAsync(CancellationToken cancellationToken)
{
return _connection ??= await base.GetConnectionAsync(cancellationToken);
}
我在 ASP.NET 6 应用程序中使用 NHibernate。出于集成测试的目的,我正在使用 SQLite 内存数据库。
这是集成测试的 NHibernate 配置的样子:
_configuration = new Configuration();
_configuration.DataBaseIntegration(db =>
{
db.Driver<SQLite20Driver>();
db.Dialect<MySqliteDialect>();
db.ConnectionProvider<SQLiteInMemoryConnectionProvider>();
db.ConnectionString = "Data Source=:memory:;Version=3;New=True;DateTimeKind=Utc;DateTimeFormatString=yyyy-MM-dd HH:mm:ss.FFFFFFF";
db.LogSqlInConsole = true;
db.ConnectionReleaseMode = ConnectionReleaseMode.OnClose;
db.HqlToSqlSubstitutions = "true=1;false=0";
db.SchemaAction = SchemaAutoAction.Validate;
});
var mapping = new ModelMapper();
mapping.AddMappings(typeof(ApplicationUserMapping).Assembly.GetTypes());
// other mappings..
var mappingDocument = mapping.CompileMappingForAllExplicitlyAddedEntities();
_configuration.AddMapping(mappingDocument);
_configuration.LinqToHqlGeneratorsRegistry<DefaultLinqToHqlGeneratorsRegistry>();
var exp = new SchemaExport(_configuration);
exp.Execute(true, true, false);
_sessionFactory = _configuration.BuildSessionFactory();
我有 SettingsService
class 有以下方法:
public async Task<IList<Setting>> GetAll()
{
using var session = _factory.OpenSession();
var settings = await session.QueryOver<Setting>().ListAsync();
return settings;
}
现在,当我从一个简单的 NUnit 测试中调用此方法时:
[Test]
public async Task GetAll()
{
var settings = await new SettingsService(_sessionFactory).GetAll();
}
我遇到一个错误:
NHibernate.Exceptions.GenericADOException : could not execute query
[ SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_ ]
[SQL: SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_]
----> System.Data.SQLite.SQLiteException : SQL logic error
no such table: Settings
整个测试的输出如下所示:
PRAGMA foreign_keys = OFF
drop table if exists Settings
// other tables drops...
PRAGMA foreign_keys = ON
create table Settings (
Id BLOB not null,
Name TEXT not null unique,
Value TEXT not null,
primary key (Id)
)
// other tables creation...
NHibernate: SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_
NHibernate.Exceptions.GenericADOException : could not execute query
[ SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_ ]
[SQL: SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_]
----> System.Data.SQLite.SQLiteException : SQL logic error
no such table: Settings
Data:
actual-sql-query: SELECT this_.Id as id1_0_0_, this_.Name as name2_0_0_, this_.Value as value3_0_0_ FROM Settings this_
at NHibernate.Loader.Loader.DoListAsync(ISessionImplementor session, QueryParameters queryParameters, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder, CancellationToken cancellationToken)
at NHibernate.Loader.Loader.ListIgnoreQueryCacheAsync(ISessionImplementor session, QueryParameters queryParameters, CancellationToken cancellationToken)
at NHibernate.Loader.Criteria.CriteriaLoaderExtensions.LoadAllToListAsync[T](IList`1 loaders, ISessionImplementor session, CancellationToken cancellationToken)
at NHibernate.Impl.SessionImpl.ListAsync[T](CriteriaImpl criteria, CancellationToken cancellationToken)
at NHibernate.Impl.SessionImpl.ListAsync[T](CriteriaImpl criteria, CancellationToken cancellationToken)
因此您可以看到 Settings
table 已创建。
如果我将 GetAll()
方法的实现更改为非异步,即不使用 ListAsync()
,而是使用 List()
函数:
public IList<Setting> GetAll()
{
using var session = _factory.OpenSession();
var settings = session.QueryOver<Setting>().List();
return settings;
}
测试通过(当然是在删除 async
、Task
和 await
之后)。
我见过 this question,但在我的例子中,唯一的区别是使用 NHibernate 的异步方法与非异步方法。我在集成测试的初始化代码和 SettingsService
.
ISessionFactory
知道这里发生了什么吗?
根据 SQLite docs:
In-Memory database ceases to exist as soon as the database connection is closed. Every :memory: database is distinct from every other. So, opening two database connections each with the filename ":memory:" will create two independent in-memory databases.
默认情况下,每次打开会话时都会创建新连接。所以这个错误是默认设置的预期行为。
但是您使用了一些自定义连接提供程序 SQLiteInMemoryConnectionProvider
,它会重用一旦打开的连接。所以我会说问题出在 SQLiteInMemoryConnectionProvider
内部——它还没有为异步代码做好准备。
确保您的连接提供程序实现了 GetConnection
和 GetConnectionAsync
方法。类似于:
public override DbConnection GetConnection()
{
return _connection ??= base.GetConnection();
}
public override async Task<DbConnection> GetConnectionAsync(CancellationToken cancellationToken)
{
return _connection ??= await base.GetConnectionAsync(cancellationToken);
}