Почему я внезапно получаю эту ошибку?

Итак, у меня есть служба WCF, внутри которой есть метод Process(). Этот метод считывает байтовый массив (файл) из одной таблицы и в основном помещает эти данные из этого файла в несколько таблиц. Он просто проходит через каждую строку. Он работает отлично с месяца в производственной среде. Внезапно он периодически перебрасывает эту ошибку:

System.InvalidOperationException: транзакция, связанная с текущим соединением, завершена, но не была удалена. Транзакция должна быть удалена до того, как соединение будет использоваться для выполнения операторов SQL.

Что-то, что может помочь: Около 2 недель назад мы сменили серверы "Производство" и "БД". Эта ошибка поднималась только после того, как мы переехали. Я никогда не сталкивался с этой проблемой, когда мы были на старых серверах. Но дело в том, что эта ошибка не возникала в первые 9-10 дней. Теперь это происходит внезапно и с перерывами. Я загрузил большие файлы (строки 1k-2.5k), и они отлично работали, и эта ошибка генерируется для гораздо меньших файлов, которые имеют 200 строк! И Служба иногда обрабатывает тот же файл.

Фрагмент кода: (он намного больше, но аналогичные операции повторяются)

using (var scope = new TransactionScope())
{
    // loop through each row/invoice
    foreach (var row in Rows)
    {
        Invoice invoice = (Invoice)CreateObjectWithConstantData(typeof(Invoice), doc, applicationName);
        invoice = (Invoice)FillObjectWithUserData(invoice, row, -1, -1, string.Empty);
        invoice.InvoiceNumber = InvoiceDBImpl.SaveInvoice(invoice, processFileRequest.RunId);

        if (invoice.InvoiceNumber == Guid.Empty)
        {
            throw new DataAccessException(string.Format(Messages.ErrorSavingInvoice, invoice.ReceiptId, invoice.ProductID));
        }
    }
}

Одна из трасс стека:

   at System.Data.SqlClient.TdsParser.TdsExecuteRPC(_SqlRPC[] rpcArray, Int32 timeout, Boolean inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, Boolean isCommandProc)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.Linq.SqlClient.SqlProvider.Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, Object[] parentArgs, Object[] userArgs, ICompiledSubQuery[] subQueries, Object lastResult)
   at System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, Object[] userArguments, ICompiledSubQuery[] subQueries)
   at System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
   at System.Data.Linq.DataContext.ExecuteMethodCall(Object instance, MethodInfo methodInfo, Object[] parameters)
   at Tavisca.TramsFileService.DataAccess.TramsDBDataContext.SaveTramsPayment(Nullable`1 paymentDate, String paymentType, Nullable`1 totalAmount, String bankAccount, String paymentMethod, String branch, String remarks, String creditCardLast4, String payeeName, String profileNumber, Nullable`1& paymentId)
   at Tavisca.TramsFileService.DataAccess.PaymentDBImpl.<>c__DisplayClass1.<SavePayment>b__0(TramsDBDataContext dc)
   at Tavisca.TramsFileService.DataAccess.SystemDataContext.PerformOperation(Action`1 action)
   at Tavisca.TramsFileService.DataAccess.PaymentDBImpl.SavePayment(Payment payment)
   at Tavisca.TramsFileService.Core.TramsFileController.ProcessFile(ProcessFileRQ processFileRequest)
   at Tavisca.TramsFileService.ServiceImplementation.TramsFileServiceImpl.ProcessFile(ProcessFileRQ processFileRequest)

Я прошел через некоторые ссылки:

Все они предлагают увеличить TimeOut на machine.config, но я не уверен, почему он работает иногда и не работает в другое время. Разве это не должно быть последовательным?

Ответ 1

Сначала, я предлагаю добавить scope.Complete(); в конце TransactionScope как:

using (var scope = new TransactionScope())
{
     //Your stuff goes here

     scope.Complete();
}

Любая транзакция должна быть зафиксирована в последней строке с использованием функции .Complete().

Во-вторых,, если увеличение TimeOut на machine.config работает, нет никакого вреда в этом, потому что для длительного файла явно требуется больше времени.

В-третьих убедитесь, что любой другой компонент внутри TransactionScope работает для всех положительных и отрицательных сценариев. По stacktrace, в частности, используется случай, когда некоторые нарушают функцию Tavisca.TramsFileService.ServiceImplementation.TramsFileServiceImpl.ProcessFile(ProcessFileRQ processFileRequest)

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

Еще одна вещь, исключение может быть законным, так как вы бросаете исключение вручную, когда invoice.InvoiceNumber == Guid.Empty, но его не упоминается, если он обрабатывается/улавливается или просто передается на верхние уровни.

Но прежде всего попробуйте добавить scope.Complete();, это само по себе может быть основной причиной.

Ответ 2

Ознакомьтесь с этой статьей подключения, которая показывает проблему в библиотеке .NET ADO.

http://connect.microsoft.com/VisualStudio/feedback/details/266095/transactionscope-timeout-issue-with-sqlconnection-and-ltm-some-operations-are-not-rolled-back

Это связано с таймаутами, которые должны быть установлены на стороне клиента (а не SQL Server).

Первая транзакция истекает, а вторая выдает сообщение об ошибке.

Используете ли вы платформу .NET 4.0?

Вот статья о том, как установить тайм-аут в коде С#.

http://paulklinker.blogspot.com/2011/08/transaction-timeouts-in-c.html

Ответ 3

  • Использовать scope.Complete(); в блоке finally, так что даже если тайм-аут исключение возникает из-за того, что сервер db занимает время, чтобы ответить вам выполнил бы транзакцию
  • Кроме того, увеличьте время ожидания, но что может помочь только в том случае, если сервер db слишком долго реагирует по какой-то причине, но может не помочь, если сервер db реагировать с перерывами.

Поскольку исключение, которое возникает, является исключением тайм-аута, которое я предлагаю, и оно происходит с перерывами без каких-либо конкретных воспроизводимых шагов, у нас есть основания полагать, что это связано с тем, что сервер db прерывается, не реагируя должным образом или в течение указанного таймаута (там могут быть проблемы с памятью или крутой момент простоя. Мы не можем сказать, что это точно, так как сервер недавно настроен. Хотя может быть причина того, что определенный тип символов не обрабатывается в данных при загрузке и перемещении, но это менее вероятно поскольку приложение было запущено и запущено задолго до смены серверов)