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

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

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

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

person Chris Canal    schedule 09.10.2008    source источник
comment
Через какое-то время есть другое решение. См. Дополнительный блок в моем ответе.   -  person Stefan Steinegger    schedule 08.04.2013


Ответы (9)


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

person Sean Carpenter    schedule 10.10.2008
comment
ага, в этом есть смысл! Даст ему шанс - person Chris Canal; 11.10.2008
comment
также взгляните на этот пример реализации DriverConnectionProvider: notepad2.wordpress.com/2008/05/19/ - person smoothdeveloper; 25.04.2009
comment
К вашему сведению, у меня возникли проблемы с поддержанием соединения с NHibernate после вызова Flush. Нашел ответ на странице bit.ly/23CRTT - person Jon Adams; 02.08.2009

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

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

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

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

person decates    schedule 22.03.2013
comment
Идеально! Это лучшее решение, другие решения часто основаны на более старых версиях sqlite, которые не обеспечивали возможность использования именованных (file: xyz.db? Mode = memory) баз данных sqlite в памяти с активным пулом соединений. - person hessenmob82; 25.08.2016
comment
Обратите внимание, что в более поздних версиях sqlite вам может потребоваться сделать что-то подобное для строки подключения (и это работает с Loquacious API): config.DataBaseIntegration(c => { /*...*/ c.ConnectionString = "DataSource=file:memdb1?mode=memory&cache=shared" }) - см. stackoverflow.com/a/12324672/489116 и комментарии, а также forum.devart.com/viewtopic.php?t=26312#p119530. - person S'pht'Kr; 04.11.2019

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

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

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


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

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

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

  • Измените строку подключения на file::memory:?cache=shared
  • Откройте соединение и создайте схему
  • Оставьте это соединение открытым до конца теста.
  • Позвольте NH создать другие соединения (нормальное поведение) во время теста.
person Stefan Steinegger    schedule 13.10.2008
comment
Используете ли вы перегрузку OpenSession для поддержки соединения или у вас есть более хитрый способ использования того же соединения? - person Graham Ambrose; 11.05.2009
comment
Я использую OpenSession (IDbConnection) и получаю соединение один раз из sessionFactory.ConnectionProvider.GetConnection (). Я, вероятно, мог бы реализовать свой собственный ConnectionProvider, тогда это было бы только вопросом конфигурации. Но до сих пор у меня не было на это времени. - person Stefan Steinegger; 11.05.2009

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

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

person Julien Bérubé    schedule 21.12.2010
comment
Пробовал это решение, но у него есть серьезный недостаток при использовании HiLo Id-Generators. При вставке большого количества новых записей в базу данных это решение ломается. Причина в том, что nhibernate может получить только одно соединение, но может потребоваться дополнительное, чтобы получить следующий HiLo-Range из БД. В более новых версиях sqlite лучшим решением является использование решения, которое определяет предоставленную ссылку. - person hessenmob82; 25.08.2016

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

person Anders B    schedule 29.01.2009
comment
Это могло быть решением в прошлом, но в новых версиях sqlite нет необходимости, поскольку они предоставляют возможность использовать общую область базы данных. Это намного проще в использовании и требует меньше инструментов. Актуальное решение см. В следующем ответе - person hessenmob82; 25.08.2016

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

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

person chills42    schedule 10.10.2008

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

person Tim Scott    schedule 22.10.2008

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

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

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

person Drexter    schedule 09.06.2013

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

person jenspo    schedule 11.01.2019