Подтвердить что ты не робот

Почему операция TransactionScope недействительна?

У меня есть процедура, которая использует рекурсивный цикл для вставки элементов в базу данных SQL Server 2005. Первый вызов, который инициирует цикл, заключен в транзакцию с использованием TransactionScope. Когда я сначала вызываю ProcessItem, данные myItem вставляются в базу данных, как и ожидалось. Однако, когда ProcessItem вызывается из ProcessItemLinks или ProcessItemComments, я получаю следующую ошибку.

"Операция недействительна для состояния транзакции"

Я запускаю это при отладке с VS 2008 в Windows 7 и запускаю MSDTC для включения распределенных транзакций. Код ниже не является моим производственным кодом, но он точно такой же. AddItemToDatabase - это метод класса, который я не могу изменить, и использует стандартный ExecuteNonQuery(), который создает соединение, затем закрывается и удаляется после завершения.

Я просмотрел другую публикацию здесь и в Интернете и до сих пор не могу решить эту проблему. Любая помощь будет высоко оценена.

using (TransactionScope processItem = new TransactionScope())
{
    foreach (Item myItem in itemsList)
    {
        ProcessItem(myItem);
    }   
    processItem.Complete();
}    
private void ProcessItem(Item myItem)
{
    AddItemToDatabase(myItem);
    ProcessItemLinks(myItem);
    ProcessItemComments(myItem);
}    
private void ProcessItemLinks(Item myItem)
{
    foreach (Item link in myItem.Links)
    {
        ProcessItem(link);
    }
}   
private void ProcessItemComments(Item myItem)
{
    foreach (Item comment in myItem.Comments)
    {
        ProcessItem(comment);
    }
}

Вот верхняя часть трассировки стека. К сожалению, я не могу показать, как это происходит, как информация о компании, которую я не могу раскрыть.

   at System.Transactions.TransactionState.EnlistPromotableSinglePhase(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Transaction atomicTransaction)
   at System.Transactions.Transaction.EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
   at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
   at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
   at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
   at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()
4b9b3361

Ответ 1

Распределенные транзакции заставили мои волосы преждевременно седеть:)

Обычные подозреваемые

  • Брандмауэр блокирует MSDTC
  • По какой-либо причине ваша транзакция временно отключена (попробуйте увеличить таймаут)
  • У вас есть другая область транзакций где-то в верхней части кода, который возится с текущей транзакцией.

Проверьте, работает ли MSDTC с помощью таких инструментов, как dtcping

Также проверьте, сначала вставив небольшое количество элементов. Ваш код, кажется, находится в рекурсивном цикле, который может обрабатывать большой объем данных. Возможно, вы работаете со многими запросами, а транзакция - с задержкой.

Когда-то System.Transactions.Transaction.Current имеет некоторые подсказки о том, что произошло. Добавьте часы против этой глобальной переменной

Ответ 2

Максимальное время ожидания по умолчанию - 10 минут. Вы можете переопределить это в файле machine.config:

<configuration>
    <system.transactions>
        <machineSettings maxTimeout="00:00:30" />
    </system.transactions>
</configuration>

Или вы можете использовать отражение, чтобы переопределить его в коде:

    private static void OverrideTransactionScopeMaximumTimeout(TimeSpan timeOut)
    {

        // 1. create a object of the type specified by the fully qualified name

        Type oSystemType = typeof(global::System.Transactions.TransactionManager);

        System.Reflection.FieldInfo oCachedMaxTimeout = oSystemType.GetField("_cachedMaxTimeout", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);

        System.Reflection.FieldInfo oMaximumTimeout = oSystemType.GetField("_maximumTimeout", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);

        oCachedMaxTimeout.SetValue(null, true);

        oMaximumTimeout.SetValue(null, timeOut);

        // For testing to confirm value was changed

        // MessageBox.Show(string.Format(&quot;DEBUG SUCCESS!! &nbsp;Maximum Timeout for transactions is &#39;{0}&#39;&quot;, TransactionManager.MaximumTimeout.ToString()));

    }

Дополнительная информация: https://blogs.msdn.microsoft.com/ajit/2008/06/18/override-the-system-transactions-default-timeout-of-10-minutes-in-the-code/