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

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

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

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

Через какое-то время есть другое решение. См. Дополнительный блок в моем ответе.

Stefan Steinegger 08.04.2013 10:57
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
33
1
16 541
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

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

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

Ответ принят как подходящий

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

ага, в этом есть смысл! Даст ему шанс

Chris Canal 11.10.2008 22:09

также взгляните на этот пример реализации DriverConnectionProvider: notepad2.wordpress.com/2008/05/19/…

smoothdeveloper 25.04.2009 12:00

К вашему сведению, у меня возникли проблемы с поддержанием соединения с NHibernate после вызова Flush. Нашел ответ на bit.ly/23CRTT

Jon Adams 02.08.2009 21:47

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

  1. Перед каждым тестом: создавайте соединение
  2. Создать схему для этого подключения
  3. Запустить тест. Одно и то же соединение используется для всех сеансов
  4. После теста: закрыть соединение

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


Редактировать

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

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

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

Используете ли вы перегрузку Open Session для обеспечения соединения или у вас есть более хитрый способ использования того же соединения?

Graham Ambrose 11.05.2009 16:38

Я использую OpenSession (IDbConnection) и получаю соединение один раз из sessionFactory.ConnectionProvider.GetConnection (). Я, вероятно, мог бы реализовать свой собственный ConnectionProvider, тогда это было бы только вопросом конфигурации. Но до сих пор у меня не было на это времени.

Stefan Steinegger 11.05.2009 17:43

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

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

Это могло быть решением в прошлом, но в новых версиях sqlite нет необходимости, поскольку они предоставляют возможность использовать общую область базы данных. Это намного проще в использовании и требует меньше инструментов. Для актуального решения см. Следующий отвечать

hessenmob82 25.08.2016 23:32

У меня были аналогичные проблемы, которые продолжались даже после открытия 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>

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

Пробовал это решение, но у него есть серьезный недостаток при использовании HiLo Id-Generators. При вставке большого количества новых записей в базу данных это решение ломается. Причина в том, что nhibernate может получить только одно соединение, но может потребоваться дополнительное, чтобы получить следующий HiLo-Range из БД. В более новых версиях sqlite лучшим решением является использование решения, которое определяет значение связь.

hessenmob82 25.08.2016 23:23

Я смог использовать базу данных SQLite в памяти и избежать необходимости перестраивать схему для каждого теста с помощью SQLite поддержка 'Shared Cache', который позволяет разделять базу данных в памяти между соединениями.

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

  • Настройте NHibernate (свободно) для использования SQLite со следующей строкой подключения:

    FullUri=file:memorydb.db?mode=memory&cache=shared
    
  • Используйте эту конфигурацию для создания объекта hbm2ddl.SchemaExport и выполнения его в отдельном соединении (но снова с той же самой строкой соединения).

  • Оставьте это соединение открытым и на него будет ссылаться статическое поле до Сборка, после чего оно будет закрыто и утилизировано. Это связано с тем, что 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, которые не обеспечивали возможность использования именованных ("file: xyz.db? Mode = memory") баз данных sqlite в памяти с активным пулом соединений.

hessenmob82 25.08.2016 23:16

Обратите внимание, что в более поздних версиях sqlite вам может потребоваться сделать что-то вроде этого для вашей строки подключения (и это работает с Loquacious API): config.DataBaseIntegration(c => { /*...*/ c.ConnectionString = "DataSource=file:memdb1?mode=memory&cache=shared" }) - см. stackoverflow.com/a/12324672/489116 и комментарии, а также форумы.devart.com/viewtopic.php?t=26312#p119530.

S'pht'Kr 04.11.2019 22:34

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

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

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

У меня такая же ошибка, когда я забыл импортировать пакет SQLite Nuget.

Другие вопросы по теме