TransactionScope вызывает блокировку?

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

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

Это внутри моего базового класса теста:

BaseScope = new CommittableTransaction(new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUnCommitted, Timeout = new System.TimeSpan(0, 5, 0) });

а затем внутри метода, который я тестирую, он:

using (TransactionScope scope = new TransactionScope())

В первый раз, когда код внутри 2-й области касается базы данных, он зависает. Есть ли у меня способ обойти эту проблему?


person Jonas    schedule 07.10.2010    source источник


Ответы (3)


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

Теперь тесты, которые вы выполняете, очень ценны, и в некоторых случаях я бы фактически сделал их вместо модульного тестирования. Я называю это ранним интеграционным тестированием (EIT). Ключевым моментом здесь является то, что мы обнаруживаем совершенно новый класс ошибок при работе с реальными объектами, а не с имитацией модульного теста. И ключ здесь - Настоящая Вещь. Как только вы подделываете среду с помощью искусственных областей транзакций и т. Д., Вы теряете большую часть преимуществ EIT, потому что вы не улавливаете тонких ошибок взаимодействия или (как в вашем случае) создаете искусственные проблемы.

Я бы нашел способ быстро заполнить базу данных достаточным количеством тестовых данных и восстановить ее до этого состояния вне теста. Сценарий «сброса в известное состояние» очень полезен для такого рода тестов.

person djna    schedule 07.10.2010
comment
Думаю, я выберу этот вариант. Я написал код для создания новой / пустой базы данных, которая запускается в начале тестов, а в конце база данных удаляется. - person Jonas; 08.10.2010

При вложении TransactionScope экземпляров вы можете получить распределенную транзакцию, а не простую локальную транзакцию. Это поведение несколько различается в зависимости от используемой базы данных. SQLServer 2008, например, не переходит в DTX, если на самом деле не задействовано несколько баз данных. Oracle, с другой стороны, всегда будет переходить к распределенной транзакции, поскольку он не может не поддерживать совместное использование соединений для одной локальной транзакции.

В зависимости от того, какую базу данных и TransactionScopeOption вы используете, вы можете зайти в тупик. Это происходит потому, что DTX часто требуют блокировок таблиц, чтобы гарантировать, что они могут быть зафиксированы атомарно. В Oracle, например, если вы запускаете DTX и вылетаете из строя или теряете соединение до его завершения, вы можете получить «Распределенную транзакцию под вопросом». Эта транзакция «В сомнении» может заблокировать одну или несколько таблиц, не позволяя другим сеансам изменять их до тех пор, пока администратор баз данных не выполнит команду ROLLBACK FORCE для идентификатора ожидающей транзакции. Некоторые базы данных (например, SQLServer) пытаются обнаружить такие взаимоблокировки и завершить одну из проблемных транзакций ... но это гарантировано.

Я бы предложил вам один из двух вариантов:

  1. Решите, действительно ли вам нужно писать тесты, которые попадают в базу данных. Часто вы можете использовать макет или заглушку, чтобы избежать необходимости писать тесты, которые изменяют, а затем откатывают базу данных. Избегать таких проблем имеет смысл, поскольку это ускоряет ваши тесты и устраняет потенциальную зависимость от них. Однако иногда вы не можете этого сделать.
  2. Если вам действительно нужно проверить свою логику на базе данных, подумайте об изменении кода, чтобы все методы использовали одно и то же соединение с базой данных для выполнения своего SQL. Это исключит создание распределенной транзакции и, надеюсь, решит вашу проблему.

Вы также можете изучить представление ожидающих транзакций своей базы данных (в Oracle это называется PENDING_TRANS $ ... в SQLServer есть XACT_STATE () функция).

person LBushkin    schedule 07.10.2010

Вам нужно будет зафиксировать базовую транзакцию, чтобы разблокировать тестовый метод, что, как я полагаю, не является желаемым поведением сортировки. Вам нужно заставить транзакцию вашего тестового метода присоединиться к внешней внешней (зонтичной / родительской / базовой / внешней) транзакции, созданной в вашем базовом классе, а не пытаться создать свою собственную.

Из MSDN CommittableTransactionClass Примечания (выделено мной):

Рекомендуется создавать неявные транзакции с использованием класса TransactionScope, чтобы окружающий контекст транзакции автоматически управлялся за вас. Также следует использовать классы TransactionScope и DependentTransaction для приложений, которые требуют использования одной и той же транзакции для нескольких вызовов функций или вызовов нескольких потоков. Дополнительные сведения об этой модели см. в Тема" Реализация неявной транзакции с использованием области транзакции ".

Создание CommittableTransaction не устанавливает автоматически внешнюю транзакцию, которая является транзакцией, в которой выполняется ваш код. Вы можете получить или установить внешнюю транзакцию, вызвав статическое свойство Current глобального объекта Transaction. Дополнительные сведения о внешних транзакциях см. В разделе «Управление потоком транзакций с использованием TransactionScopeOption» раздела «Реализация неявной транзакции с использованием области действия транзакции». Если внешняя транзакция не установлена, любая операция в диспетчере ресурсов не является частью этой транзакции. Вам необходимо явно установить и сбросить внешнюю транзакцию, чтобы гарантировать, что менеджеры ресурсов работают в правильном контексте транзакции.

Пока транзакция CommittableTransaction не была зафиксирована, все ресурсы, связанные с транзакцией, по-прежнему заблокированы.

Как указал djna, использование транзакций для отката изменений, сделанных во время тестирования, является довольно оскорбительным. Вы должны хорошо разбираться в тестировании и отменять и вносить изменения в базу данных в предложении finally, чтобы другие тесты, которые могут выполняться после этого, никогда не затрагивались. Если у вас есть много тестов, которые уже не соответствуют требованиям, то вы, вероятно, не пойдете по этому пути сейчас. В этом случае измените свою базу, чтобы использовать неявные транзакции с областью действия RequiresNew, а в вашем методе тестирования используйте Required.

person Mike Atlas    schedule 07.10.2010
comment
Я просто хочу отметить, что мое отрицательное голосование по этому поводу совершенно не связано и, похоже, было голосованием мести, поскольку я получил строку из 5 отрицательных голосов одновременно на более старые ответы: / - person Mike Atlas; 31.10.2010
comment
Спасибо, Дамиан :) Я почти уверен, что мой ответ чертовски хороший. - person Mike Atlas; 04.12.2010