Подтвердить что ты не робот

Проблема с использованием SQLite: память: с NHibernate

Я использую NHibernate для моей dataacess, и некоторое время я не использовал SQLite для локальных тестов интеграции. Я использую файл, но я думал, что воспользуюсь опцией: memory:. Когда я запускаю какие-либо интеграционные тесты, создается база данных (NHibernate выплевывает sql-таблицу sql), но взаимодействие с базой данных вызывает ошибку.

Кто-нибудь получил NHibernate, работающий с базой данных в памяти? Возможно ли это? Строка подключения, которую я использую, такова:

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

Ответ 1

База данных SQLite памяти существует только в том случае, если соединение с ней остается открытым. Использовать его в модульных тестах с NHibernate:
1. Откройте ISession в начале теста (возможно, в методе [SetUp]).
2. Используйте соединение с этим сеансом в вызове SchemaExport.
3. Используйте тот же сеанс в своих тестах.
4. Завершите сеанс в конце теста (возможно, в методе [TearDown]).

Ответ 2

Мне удалось использовать базу данных SQLite в памяти и избежать необходимости перестраивать схему для каждого теста с помощью поддержки SQLite для "общего кэша" , что позволяет база данных в памяти, которая должна быть разделена между соединениями.

Я сделал следующее в AssemblyInitialize (я использую MSTest):

  • Настройте NHibernate (Fluent) для использования 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 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;
        }
    }
}

И базовый класс для каждого класса unit test/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();
    }
}

Управление ресурсами может улучшиться, я признаю, но это все-таки модульные тесты (предлагаемые улучшения приветствуются!).

Ответ 3

Мы используем SQLite в памяти для всех наших тестов базы данных. Мы используем одно соединение ADO для тестов, которые повторно используются для всех сеансов NH, открытых одним и тем же тестом.

  • Перед каждым тестом: создать соединение
  • Создать схему в этом соединении
  • Выполнить тест. То же соединение используется для всех сеансов.
  • После теста: закрыть соединение

Это позволяет также выполнять тесты с несколькими включенными сеансами. SessionFactory также создается один раз для всех тестов, потому что чтение файлов сопоставления занимает довольно много времени.


Изменить

Использование общего кэша

Так как System.Data.Sqlite 1.0.82 (или Sqlite 3.7.13), существует Общий кэш, который позволяет нескольким соединениям использовать одни и те же данные, а также для Базы данных с памятью. Это позволяет создавать базу данных в памяти в одном соединении и использовать ее в другом. (Я еще не пробовал, но теоретически это должно работать):

  • Измените строку подключения на file::memory:?cache=shared
  • Откройте соединение и создайте схему
  • Держите это соединение открытым до конца теста
  • Пусть NH создает другие соединения (нормальное поведение) во время теста.

Ответ 4

У меня были аналогичные проблемы, которые продолжались даже после открытия ISession, как указано выше, и добавили в мою строку подключения "Pooling = True; Max Pool Size = 1". Это помогло, но у меня все еще были случаи, когда соединение закрывалось во время теста (обычно сразу после совершения транзакции).

Что в конечном итоге сработало для меня, это установить свойство "connection.release_mode" в "on_close" в моей конфигурации SessionFactory.

Моя конфигурация в файле 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>

Надеюсь, что это поможет!

Ответ 5

Просто дикая догадка, но это вывод sql от NHibernate, используя команду, не поддерживаемую sqlite?

Также, что произойдет, если вы используете файл вместо памяти? (System.IO.Path.GetTempFileName() будет работать, я думаю...)

Ответ 6

Я делаю это с Rhino Commons. Если вы не хотите использовать Rhino Commons, вы можете изучить источник, посмотрите, как он это делает. Единственная проблема, с которой я столкнулся, заключается в том, что SQLite не поддерживает вложенные транзакции. Это заставило меня изменить мой код для поддержки тестирования интеграции. Интеграционное тестирование в базе данных памяти настолько потрясающе, что я решил, что это был честный компромисс.

Ответ 7

У меня были проблемы с базами данных SQLite. Итак, теперь мы используем SQLite, работающий с файлами на диске ramdrive.

Ответ 8

Просто хочу поблагодарить декады. Пытаюсь решить это уже пару месяцев, и все, что я должен был сделать, это добавить

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

в строку подключения в файле конфигурации nhibernate. Также, используя только NHibernate с *.hbm.xml, а не FNH, и мне действительно не нужно было модифицировать мой код вообще!

Ответ 9

Я получил ту же ошибку, когда забыл импортировать пакет SQLite Nuget.