为什么我突然遇到这个错误?

11
所以我有一个WCF服务,在其中有一个Process()方法。该方法从一个表中读取一个字节数组(文件),并将该文件的数据放入多个表中。它只是遍历每一行。在生产环境中,自一个月以来它一直工作得很好。现在突然间,它会偶然抛出以下错误: 可能有帮助的信息: 大约两周前,我们更换了生产Web和DB服务器。只有在我们迁移后才会抛出此错误。当我们使用旧服务器时,我从未遇到过这个问题。但问题是,在前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)

我查看了一些链接:

  1. 链接1
  2. 链接2
  3. 链接3

它们都建议增加 machine.config 中的 TimeOut,但我不确定为什么有时候可以使用,而有时候则不行。这不应该是一致的吗?


似乎有一些错误破坏了事务。你确定你确实没有发生这样的错误吗?我经常看到人们吞掉异常。 - usr
或者,某些东西过早地调用了Complete方法。可能是这个原因吗? - usr
@usr: 我明白你的意思了。我在我的事务中使用了一个using块,在该块的末尾我调用了transaction.Complete()。在中间,我有一些代码来抛出错误,如果有任何东西无法保存的话。可能是在保存时抛出了错误,而事务没有回滚吗? - karan k
好的,那么请尝试执行 WAITFOR DELAY '00:00:40' 并将事务超时设置为 30,命令超时设置为无限制(0)以重现问题。同时,请尝试使用 SELECT 1/0 进行复现。 - usr
你是否可能在客户端事务范围内调用服务方法? - Maarten
显示剩余3条评论
3个回答

8
首先,我建议在TransactionScope结束时添加scope.Complete();,例如:
using (var scope = new TransactionScope())
{
     //Your stuff goes here

     scope.Complete();
}

任何交易都必须使用 .Complete() 函数在最后一行提交。

其次,如果在 machine.config 上增加 TimeOut 可以解决问题,那么没有什么害处,因为长文件显然需要更多时间。

第三,确保在 TransactionScope 内调用的任何其他组件都适用于所有正面和负面情况。从堆栈跟踪来看,在特定用例中,某些内容在函数 Tavisca.TramsFileService.ServiceImplementation.TramsFileServiceImpl.ProcessFile(ProcessFileRQ processFileRequest) 中出现了故障。

还要确保如果任何底层调用中使用了存储过程,则在存储过程内部发生的任何失败事务也可能会导致 TransactionScope 失败。

还有一件事,抛出的异常也可能是合法的,因为当 invoice.InvoiceNumber == Guid.Empty 时手动抛出异常,但没有说明它是否被处理/捕获或只是传递给上层。

但首先尝试添加scope.Complete();,这可能是根本原因。


6
请查看这篇与.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框架?以下是一篇关于如何在C#代码中设置超时的文章。
链接如下:http://paulklinker.blogspot.com/2011/08/transaction-timeouts-in-c.html

1
  1. 在finally块中使用scope.Complete();,即使由于数据库服务器响应时间过长而发生超时异常,您也可以完成事务
  2. 此外,增加超时时间可能只有在数据库服务器因某种原因而需要很长时间才能响应时才有用,但如果数据库服务器间歇性地变得不响应,则可能无效。

由于发生的异常是超时异常,并且没有特定的可重现步骤,我们有理由相信这是由于数据库服务器间歇性地未能适当地响应或在指定的超时时间内未能响应(可能存在内存问题或突然的停机。我们不能确定,因为服务器是新设置的。虽然在加载和移动数据时可能存在一种特定类型的字符未处理,但这种情况不太可能发生,因为应用程序在更改服务器之前已经运行了很长时间)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接