Фон:

Моя команда стремится обеспечить успешное выполнение компиляции кода и модульных тестов сразу после оформления заказа. Чтобы облегчить это и протестировать некоторые из наших сопоставлений NHibernate, мы добавили базу данных SQLite в наш репозиторий, которая является зеркалом нашей производственной базы данных SQL Server 2005. Мы используем последние версии: MbUnit3 (часть Gallio), System.Data.SQLite и NHibernate.

Проблема:

Я обнаружил, что следующий модульный тест не работает с SQLite, несмотря на то, что он выполняется без проблем с SQL Server 2005.

    [Test]
    [Rollback]
    public void CompleteCanPersistNode()
    {
        // returns a Configuration for either SQLite or SQL Server 2005 depending on how the project is configured.
        Configuration config = GetDbConfig(); 

        ISessionFactory sessionFactory = config.BuildSessionFactory();
        ISession session = sessionFactory.OpenSession();

        Node node = new Node();
        node.Name = "Test Node";
        node.PhysicalNodeType = session.Get<NodeType>(1);

        // SQLite fails with the exception below after the next line called.
        node.NodeLocation = session.Get<NodeLocation>(2);

        session.Save(node);
        session.Flush();

        Assert.AreNotEqual(-1, node.NodeID);
        Assert.IsNotNull(session.Get<Node>(node.NodeID));
    }

Исключение, которое я получаю (ТОЛЬКО при работе с SQLite), следующее:

NHibernate.ADOException: cannot open connection --->
System.Data.SQLite.SQLiteException:
  The database file is locked database is locked
    at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
    at System.Data.SQLite.SQLiteDataReader.NextResult()
    at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
    at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
    at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
    at System.Data.SQLite.SQLiteTransaction..ctor(SQLiteConnection connection, Boolean deferredLock)
    at System.Data.SQLite.SQLiteConnection.BeginDbTransaction(IsolationLevel isolationLevel)
    at System.Data.SQLite.SQLiteConnection.BeginTransaction()
    at System.Data.SQLite.SQLiteEnlistment..ctor(SQLiteConnection cnn, Transaction scope)
    at System.Data.SQLite.SQLiteConnection.EnlistTransaction(Transaction transaction)
    at System.Data.SQLite.SQLiteConnection.Open()
    at NHibernate.Connection.DriverConnectionProvider.GetConnection()
    at NHibernate.Impl.SessionFactoryImpl.OpenConnection()
    --- End of inner exception stack trace ---
    at NHibernate.Impl.SessionFactoryImpl.OpenConnection()
    at NHibernate.AdoNet.ConnectionManager.GetConnection()
    at NHibernate.AdoNet.AbstractBatcher.Prepare(IDbCommand cmd)
    at NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd)
    at NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection, ISessionImplementor session)
    at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
    at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
    at NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister)
    at NHibernate.Loader.Entity.AbstractEntityLoader.Load(ISessionImplementor session, Object id, Object optionalObject, Object optionalId)
    at NHibernate.Loader.Entity.AbstractEntityLoader.Load(Object id, Object optionalObject, ISessionImplementor session)
    at NHibernate.Persister.Entity.AbstractEntityPersister.Load(Object id, Object optionalObject, LockMode lockMode, ISessionImplementor session)
    at NHibernate.Event.Default.DefaultLoadEventListener.LoadFromDatasource(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options)
    at NHibernate.Event.Default.DefaultLoadEventListener.DoLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options)
    at NHibernate.Event.Default.DefaultLoadEventListener.Load(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options)
    at NHibernate.Event.Default.DefaultLoadEventListener.ProxyOrLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options)
    at NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType)
    at NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType)
    at NHibernate.Impl.SessionImpl.Get(String entityName, Object id)
    at NHibernate.Impl.SessionImpl.Get(Type entityClass, Object id)
    at NHibernate.Impl.SessionImpl.Get[T](Object id)
D:\dev\598\Code\test\unit\DataAccess.Test\NHibernatePersistenceTests.cs

Когда используется SQLite и НЕ указан атрибут [Rollback], тест также завершается успешно.

Вопрос:

Это проблема с реализацией TransactionScope в System.Data.SQLite, которую использует MbUnit3 для [отката], или с ограничением механизма SQLite?

Есть ли способ написать этот модульный тест, работающий против SQLite, который будет выполнять откат, чтобы не влиять на базу данных при каждом запуске теста?

2
antik 27 Мар 2009 в 22:21

3 ответа

Лучший ответ

Убедитесь, что вы не пропустили connection.release_mode=on_close в конфигурации SQLite NHibernate. (справочные документы)

Кстати: всегда выбрасывайте свои ISession и ISessionFactory.

1
Mauricio Scheffer 28 Мар 2009 в 06:13
Хороший звонок - я видел эту часть документа несколько дней назад, но это не показалось мне проблемой, которую я сейчас наблюдаю. Изменение моего release_mode исправило тесты. Спасибо!
 – 
antik
30 Мар 2009 в 19:01

Это не настоящий ответ на ваш вопрос, но, вероятно, решение проблемы.

Я использую реализацию sql lite в памяти для своих интеграционных тестов. Я создаю схему и заполняю базу данных перед каждым тестом. Создание схемы и начальное заполнение данных происходит очень быстро (менее 0,01 секунды на тест), потому что это база данных в памяти.

Почему вы используете физическую базу данных?

Изменить: ответ на ответ на вопрос выше:

1.) Потому что я перенес свою схему и данные непосредственно из SQL Server 2005, и я хочу, чтобы они сохранялись в системе управления версиями.

  • Я рекомендую сохранить файл со схемой базы данных, а также файл или сценарий, который создает образцы данных в системе управления версиями. Вы можете сгенерировать файл с помощью sql server studion management express, вы можете сгенерировать его из ваших сопоставлений NHibernate или вы можете использовать такой инструмент, как sql compare, и вы, вероятно, сможете найти другие решения для этого, когда вам это нужно. Простые текстовые файлы легче хранить в системах контроля версий, чем полные двоичные файлы базы данных.

2.) Отличается ли что-то в движке SQLite в памяти таким образом, чтобы решить эту проблему?

  • Это может решить ваши проблемы, потому что вы можете воссоздавать свою базу данных перед каждым тестом. Ваша тестируемая база данных будет находиться в ожидаемом состоянии перед выполнением каждого теста. Преимущество этого заключается в том, что нет необходимости откатывать ваши транзакции, но я провел аналогичный тест с sqllite в памяти, и он работал должным образом.
2
Paco 28 Мар 2009 в 01:57
Потому что я перенес свою схему и данные непосредственно из SQL Server 2005 и хочу, чтобы они сохранялись в системе управления версиями. Отличается ли что-то в движке SQLite в памяти таким образом, чтобы решить эту проблему?
 – 
antik
27 Мар 2009 в 22:52
Пако - Какую базу данных SQL в памяти вы использовали? Я думаю, что этот вариант, безусловно, стоит изучить.
 – 
Zach Burlingame
28 Мар 2009 в 03:30
Ах, теперь я понимаю, насколько это полезно. Отсутствие необходимости в откате может решить проблему, потому что тесты работают нормально, если откат не используется. Спасибо за дополнительную информацию: это огромная помощь!
 – 
antik
29 Мар 2009 в 03:24

Откажитесь от [отката] и используйте NDbUnit. Я сам использую это для этого точного сценария, и он отлично работает.

0
remotefacade 27 Мар 2009 в 22:40
NDbUnit.org не работает с середины февраля, когда мы выбрали фреймворки. Моя самая большая жалоба на Gallio - это отсутствие документации, и если там нет документации, которую я не вижу, это шаг назад даже от того места, где я нахожусь сейчас.
 – 
antik
27 Мар 2009 в 22:49
Да, NDbUnit был неактивен в течение долгого времени, хотя в последнее время на сайте кода Google были некоторые действия. пожал плечами Я прошел через ту же борьбу с MbUnit / Gallio, но так и не смог заставить его работать должным образом. Надеюсь, кто-то еще сможет показать нам свет :)
 – 
remotefacade
27 Мар 2009 в 22:55