Почему операция 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()
Ответы
Ответ 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("DEBUG SUCCESS!! Maximum Timeout for transactions is '{0}'", TransactionManager.MaximumTimeout.ToString()));
}
Дополнительная информация: https://blogs.msdn.microsoft.com/ajit/2008/06/18/override-the-system-transactions-default-timeout-of-10-minutes-in-the-code/