"Это SqlTransaction завершено, оно больше не используется".... ошибка конфигурации?
Я работаю над этим около полутора лет и искал многочисленные блоги и помогал статьям в Интернете. Я нашел несколько вопросов о SO, связанных с этой ошибкой, но я не думал, что они вполне применимы к моей ситуации (или, в некоторых случаях, к сожалению, я не мог понять их достаточно хорошо, чтобы реализовать: P). Я не уверен, что могу описать это достаточно хорошо для помощи... но здесь идет:
У нас есть приложение .NET для отслеживания наших ресурсов. Есть функция экспорта для копирования ресурса в систему отслеживания времени и биллинговую систему; это позволяет получить доступ к хранимой процедуре, которая связывается с базами данных времени и биллинга.
Недавно я перевел базу данных биллинговой системы на новый сервер (исходный сервер: Server 2003 SP2, SQL 2005, новый сервер: Server 2008 R2, SQL 2008 R2). У меня установлен Linked Server, который указывает на базы данных 2008 года. Я обновил хранимую процедуру, указав на сервер 2008 года, а затем получил сообщение об MSDTC и RPC (http://www.safnet.com/writing/tech/archives/2007/06/server_myserver.html). Я включил "rpc/rpc out" на Linked Server и установил MSDTC для разрешения доступа к сети (что-то вроде этого: http://www.sqlwebpedia.com/content/msdtc-troubleshooting).
Теперь я получаю выше, когда я пытаюсь запустить функцию экспорта: "Это SqlTransaction завершено, оно больше не используется". Мне кажется странным, что когда я просто запускаю хранимую процедуру (из SSMS), она говорит, что она успешно завершена.
Кто-нибудь видел это раньше? Я что-то пропустил в конфигурации? Я продолжаю перебирать те же страницы, и единственное, что я нашел, это то, что я не перезагружался после внесения изменений в MSDTC (см. Здесь: http://social.msdn.microsoft.com/forums/en-US/adodotnetdataproviders/thread/7172223f-acbe-4472-8cdf-feec80fd2e64/).
Я могу опубликовать часть или всю хранимую процедуру, если это поможет... пожалуйста, дайте мне знать.
Ответы
Ответ 1
Я считаю, что это сообщение об ошибке связано с "транзакцией зомби".
Найдите возможные области, в которых трансактон совершается дважды (или откатывается дважды, или откатывается назад, и совершается, и т.д.). Является ли код .Net фиксировать транзакцию после того, как SP уже ее совершил? Заставляет ли код .Net откатываться назад при столкновении с ошибкой, а затем попытаться отбросить его обратно в предложение catch (или finally)?
Возможно, на старом сервере никогда не попадало условие ошибки, и поэтому ошибочный код "двойного отката" никогда не попадал. Возможно, теперь у вас есть ситуация, когда на новом сервере возникла некорректная ошибка, и теперь код ошибки попадает через обработку исключений.
Можно ли отладить код ошибки? У вас есть трассировка стека?
Ответ 2
У меня это было недавно после рефакторинга в новом диспетчере соединений. Новая процедура приняла транзакцию, поэтому ее можно было запустить как часть пакета, проблема заключалась в использовании блока:
public IEnumerable<T> Query<T>(IDbTransaction transaction, string command, dynamic param = null)
{
using (transaction.Connection)
{
using (transaction)
{
return transaction.Connection.Query<T>(command, new DynamicParameters(param), transaction, commandType: CommandType.StoredProcedure);
}
}
}
Похоже, что внешнее использование закрывало базовое соединение, поэтому любые попытки совершить или отменить транзакцию запустили сообщение "This SqlTransaction has completed; it is no longer usable."
Я удалил добавленные дополнения, и проблема исчезла.
public IEnumerable<T> Query<T>(IDbTransaction transaction, string command, dynamic param = null)
{
return transaction.Connection.Query<T>(command, new DynamicParameters(param), transaction, commandType: CommandType.StoredProcedure);
}
Проверить все, что может закрыть соединение, в контексте транзакции.
Ответ 3
Недавно я столкнулся с подобной ситуацией. Чтобы отлаживать любую версию VS IDE, открывайте исключения из Debug (Ctrl + D, E) - отметьте все флажки напротив столбца "Брошено" и запустите приложение в режиме отладки. Я понял, что одна из таблиц не была импортирована должным образом в новую базу данных, поэтому внутреннее исключение Sql исключало соединение, что приводит к этой ошибке.
Суть истории в том, что если предыдущий рабочий код возвращает эту ошибку в новой базе данных, это может быть проблема с отсутствием схемы базы данных, реализуемой над подсказкой для отладки,
Надеюсь, что это поможет,
HydTechie
Ответ 4
В моем случае проблема заключалась в том, что один из запросов, включенных в транзакцию, вызывал исключение, и хотя исключение было "грациозно" обработано, ему все же удалось отменить всю транзакцию.
Мой псевдокод был похож:
var transaction = connection.BeginTransaction();
for(all the lines in a file)
{
try{
InsertLineInTable(); // INSERT statement might fail and throw an exception
}
catch {
// notify the user about the error on line x and continue
}
}
// Commit and Rollback will fail if one of the queries
// in InsertLineInTable threw an exception
if(CheckTableForErrors())
{
transaction.Commit();
}
else
{
transaction.Rollback();
}
Ответ 5
У меня та же проблема. Эта ошибка возникает из-за объединения пулов. Когда существует два или более пользователей, которые не могут использовать систему, объединение пулов повторно использует связь и трансацию. Если первый пользователь выполнил отмену транзакции, транзакция не будет использоваться долго.
Ответ 6
Также проверьте, выполняются ли какие-либо длительные процессы, выполненные из вашего приложения .NET, с помощью БД. Например, вы можете вызвать хранимую процедуру или запрос, который не имеет достаточного времени для завершения, которое может отображаться в ваших журналах, как:
Проверьте настройки тайм-аута команды
Попробуйте запустить трассировку (профайлер) и посмотреть, что происходит на стороне БД...
Ответ 7
Вот способ обнаружения транзакции Zombie
SqlTransaction trans = connection.BeginTransaction();
//some db calls here
if (trans.Connection != null) //Detecting zombie transaction
{
trans.Commit();
}
Декомпилируя класс SqlTransaction, вы увидите следующее
public SqlConnection Connection
{
get
{
if (this.IsZombied)
return (SqlConnection) null;
return this._connection;
}
}
Я замечаю, что соединение закрыто, transOP станет зомби, поэтому не может Commit
.
Для моего случая это связано с тем, что у меня есть Commit()
внутри блока finally
, тогда как соединение было в блоке try
. Это устройство вызывает подключение и сбор мусора. Решением было поставить Commit
внутри блока try
.