Я понимаю, что этот вопрос задавал раньше, но мне еще предстоит найти ответ, который действительно работает.
Моя проблема возникает, когда мои модульные тесты пытаются вызвать веб-службу, которая использует Linq для запроса базы данных.
unit test настроен следующим образом:
[TestInitialize]
public void SetUp()
{
var scope = new TransactionScope(TransactionScopeOption.Required,TimeSpan.MaxValue);
var database = new DatabaseDataContext();
}
[TestCleanup]
public void TearDown()
{
scope.Dispose();
database.Dispose();
}
[TestMethod]
public void GetCategoryList_Success()
{
// create test data
var result = service.GetItems();
}
Метод service.GetItems выглядит так:
try
{
using (DatabaseDataContext database = new DatabaseDataContext())
{
var items = (from i in database.Items
select i).ToList<Items>();
return items;
}
}
catch (Exception ex)
{
// log error
return null;
}
Когда запрос Linq пытается выполнить, генерируется следующее исключение:
Операция недействительна для состояния транзакции.
Я полагаю, что это связано с вложенными транзакциями, но мне нужно сохранить транзакцию в открытом классе тестов, чтобы тестовые данные фактически не сохранялись в базе данных, и я могу избавиться от нее после запуска теста.
Кроме того, мой хостинг находится на общем экземпляре, поэтому я не могу напрямую вносить изменения в сервер.
Есть ли способ заставить это работать как есть или, альтернативно, есть альтернатива использованию TransactionScope в этом контексте?
Вы пытаетесь создать два соединения с одной базой данных в одном TransactionScope - одно соединение для тестового процесса и другое для процесса веб-службы. Это не работает, потому что в базе данных tranasctions из разных процессов явно не могут быть вложенными.
Вы должны изменить свой подход к тестированию здесь. Прочтите ответ Джона.
В качестве обходного пути вы можете заставить свой веб-сервис управлять транзакцией. Например, метод BeginGlobalTran
должен создать глобальный объект Transaction (или TransactionScope) для веб-службы (хранимый в свойстве), все остальные его методы должны использовать этот объект (так что все другие транзакции вложены в глобальную транзакцию), другой метод говорит, что RollbackGlobalTran
должен отменить эту глобальную транзакцию.
При этом это обходное решение, и это может вызвать некоторые неожиданные побочные эффекты (например, ошибка в методе сделает транзакцию "global" несовместимой, поэтому вам придется перезапустить ее, чтобы избежать дальнейших ошибок).