【问题标题】:NHibernate tests with SQLite - no such table error when using async methods使用 SQLite 进行 NHibernate 测试 - 使用异步方法时没有此类表错误
【发布时间】:2022-01-21 08:27:28
【问题描述】:

我在我的 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 类,它有以下方法:

    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表已经创建好了。

如果我将GetAll() 方法的实现改为非异步,即不使用ListAsync(),而是使用List() 函数:

    public IList<Setting> GetAll()
    {
        using var session = _factory.OpenSession();
        var settings = session.QueryOver<Setting>().List();
        return settings;
    }

测试通过(当然,在从中删除 asyncTaskawait 之后)。

我见过this question,但就我而言,唯一的区别是使用NHibernate 的异步与非异步方法。我在集成测试的初始化代码和SettingsService 内部使用了相同的ISessionFactory

知道这里发生了什么吗?

【问题讨论】:

    标签: c# asp.net sqlite unit-testing nhibernate


    【解决方案1】:

    根据 SQLite docs:

    一旦数据库连接,内存数据库就不再存在 已经关闭。每个 :memory: 数据库都彼此不同。所以, 打开两个数据库连接,每个连接的文件名为“:memory:” 将创建两个独立的内存数据库。

    默认情况下,每次打开会话时 - 都会创建新连接。所以这个错误是默认设置下的预期行为。

    但是您使用了一些自定义连接提供程序SQLiteInMemoryConnectionProvider,它重用了一次打开的连接。所以我想说问题出在SQLiteInMemoryConnectionProvider 内部——它还没有为异步代码做好准备。

    确保您的连接提供程序同时实现 GetConnectionGetConnectionAsync 方法。比如:

    public override DbConnection GetConnection()
    {
        return  _connection ??= base.GetConnection();
    }
    
    public override async Task<DbConnection> GetConnectionAsync(CancellationToken cancellationToken)
    {
        return  _connection ??= await base.GetConnectionAsync(cancellationToken);
    }
    

    【讨论】:

    • 嘿@Roman Artiukhin,这是一个有趣的见解!这是我的 SQLiteInMemoryConnectionProvider:gist.github.com/dsibinski/b1c807d4eaaf6ab81560d6aca58d93f2 我将其更改为您的建议:gist.github.com/dsibinski/da58a09912762d72601f923104f51cdc 但是,在初始化数据库以进行测试时,最后一行:_sessionFactory = _configuration.BuildSessionFactory();抛出一个带有验证错误的异常 - 所有表都不存在......所以我想还有更多工作要做来调整初始化代码以异步 NHibernate。
    • 不需要建议的更改。静态字段应该可以工作。看起来 GetConnectionAsync 覆盖丢失了
    猜你喜欢
    • 2018-08-05
    • 1970-01-01
    • 1970-01-01
    • 2021-08-05
    • 2010-09-30
    • 2010-11-13
    • 1970-01-01
    • 1970-01-01
    • 2013-06-28
    相关资源
    最近更新 更多