"Это 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, с помощью БД. Например, вы можете вызвать хранимую процедуру или запрос, который не имеет достаточного времени для завершения, которое может отображаться в ваших журналах, как:

  • Истекло время ожидания выполнения. Период ожидания, прошедший до завершение операции или сервер не отвечает.

    • Это SqlTransaction завершено; он больше не может использоваться.

Проверьте настройки тайм-аута команды Попробуйте запустить трассировку (профайлер) и посмотреть, что происходит на стороне БД...

Ответ 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.