Почему операция TransactionScope недопустима? - PullRequest
12 голосов
/ 23 марта 2010

У меня есть подпрограмма, которая использует рекурсивный цикл для вставки элементов в базу данных 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()

Ответы [ 2 ]

24 голосов
/ 23 марта 2010

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

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

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

Проверьте, работает ли MSDTC нормально, используя такие инструменты, как dtcping

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

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

2 голосов
/ 18 октября 2016

Максимальное время ожидания по умолчанию составляет 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/

...