我们遇到了EF Core的超时异常.

我们有一些正在工作的查询和一些不起作用的查询.我们正在将Azure Container应用程序与内部部署的SQL Server结合使用.

代码:这是我们正在执行的查询,它是不起作用的查询之一:

IQueryable<Disbursement> response = _dataContext.Disbursements
    .Where(w => w.DisbursementDate <= disbursementDate && !w.DeletedFlag && !w.Paid && w.MethodId == methodId && w.AccountId == accountId)
    .OrderByDescending(o => o.CreatedOnDateUtc);

var result = response.Skip((page - 1) * pageSize)
    .Take(pageSize)
    .ToList();

以下是一个工作查询的示例

IQueryable<AccountBillingModel> response = _dataContext.Disbursements.Where(w => w.DisbursementDate <= disbursementDate && !w.DeletedFlag && !w.Paid && w.MethodId == methodId)
    .Select(s => s.AccountId)
    .Distinct()
    .Select(s => new AccountBillingModel
    {
        AccountId = s
    });

return await response.ToListAsync();

这是模型

public class Disbursement
{
    public Guid Id { get; set; }
    public Guid TransactionId { get; set; }
    public Guid AccountId { get; set; }
    public Guid MethodId { get; set; }
    public string ExternalItemId { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public int Quantity { get; set; }
    public double PriceExcluding { get; set; }
    public bool Paid { get; set; }
    public DateTime DisbursementDate { get; set; }
    public DateTime? DisbursementRunDate { get; set; }
    public DateTime? AmendedOnDateUtc { get; set; }
    public bool DeletedFlag { get; set; } = false;
    public DateTime CreatedOnDateUtc { get; set; } = DateTime.UtcNow;
}

DbContext:

public class DataContext : DbContext
{
    public DataContext(DbContextOptions options) : base(options) { }
    public DbSet<Disbursement> Disbursements { get; set; }
}

如果我们使用docker在本地机器上运行这些代码,它就可以工作.

我们已经试过了.

  • 实体框架核心8

  • 实体框架核心6

  • .NET core 6

  • .NET core 8

我们甚至try 将异步呼叫(如ToListAsync())更改为正常的ToList(),但没有成功

我们所看到的是,如果我们使模型更小,即删除查询工作的一些列,基本上就像总是工作的第二个查询只 Select 一列.

一旦我们达到大约10列,我们就开始经历超时.当我们将模型保留为原样时,我们看到的是相同的情况,但只指定了10列作为Select()列的一部分

表定义:

CREATE TABLE [dbo].[Disbursements] 
(
    [Id]                UNIQUEIDENTIFIER CONSTRAINT [DF_Disbursements_Id] DEFAULT (newid()) NOT NULL,
    [AccountId]         UNIQUEIDENTIFIER NOT NULL,
    [MethodId]          UNIQUEIDENTIFIER NOT NULL,
    [ExternalItemId]    NVARCHAR(50)     NOT NULL,
    [Description]       NVARCHAR(150)    NOT NULL,
    [Quantity]          INT              NOT NULL,
    [PriceExcluding]    FLOAT (53)       NOT NULL,
    [DisbursementDate]  DATETIME         NOT NULL,
    [Paid]              BIT              CONSTRAINT [DF_Disbursements_Paid] DEFAULT ((0)) NOT NULL,
    [AmendedOnDateUtc]  DATETIME         NULL,
    [CreatedOnDateUtc]  DATETIME         NOT NULL,
    [DeletedFlag]       BIT              CONSTRAINT [DF_Disbursements_DeletedFlag] DEFAULT ((0)) NOT NULL,
    [DisbursementRunDate] DATETIME NULL, 

    CONSTRAINT [PK_Disbursements] PRIMARY KEY CLUSTERED ([Id] ASC)
);

堆栈跟踪

An exception occurred while iterating over the results of a query for context type \u0027****\u0027.\nMicrosoft.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.\n ---\u003E System.ComponentModel.Win32Exception (258): Unknown error 258\n  
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action\u00601 wrapCloseInAction)\n  
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.ThrowExceptionAndWarning(Boolean callerHasConnectionLock, Boolean asyncClose)\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte\u0026 value)\n  
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean\u0026 dataReady)\n  
   at Microsoft.Data.SqlClient.SqlDataReader.TryConsumeMetaData()\n  
   at Microsoft.Data.SqlClient.SqlDataReader.get_MetaData()\n  
   at Microsoft.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)\n  
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean isAsync, Int32 timeout, Task\u0026 task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)\n  
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource\u00601 completion, Int32 timeout, Task\u0026 task, Boolean\u0026 usedCache, Boolean asyncWrite, Boolean inRetry, String method)\n  
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)\n  
   at Microsoft.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)\n  
   at Microsoft.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)\n  
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)\n  
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable\u00601.Enumerator.InitializeReader(Enumerator enumerator)\n  
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable\u00601.Enumerator.\u003C\u003Ec.\u003CMoveNext\u003Eb__19_0(DbContext _, Enumerator enumerator)\n  
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func\u00603 operation, Func\u00603 verifySucceeded)\n  
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable\u00601.Enumerator.MoveNext()\nClientConnectionId:038757a9-2247-477d-98c5-31eacfa8d39d\nError Number:-2,State:0,Class:11",
  "Exception": "Microsoft.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.\n ---\u003E System.ComponentModel.Win32Exception (258): Unknown error 258\n  
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action\u00601 wrapCloseInAction)\n  
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.ThrowExceptionAndWarning(Boolean callerHasConnectionLock, Boolean asyncClose)\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()\n  
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte\u0026 value)\n  
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean\u0026 dataReady)\n  
   at Microsoft.Data.SqlClient.SqlDataReader.TryConsumeMetaData()\n  
   at Microsoft.Data.SqlClient.SqlDataReader.get_MetaData()\n  
   at Microsoft.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)\n  
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean isAsync, Int32 timeout, Task\u0026 task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)\n  
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource\u00601 completion, Int32 timeout, Task\u0026 task, Boolean\u0026 usedCache, Boolean asyncWrite, Boolean inRetry, String method)\n  
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)\n  
   at Microsoft.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)\n  
   at Microsoft.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)\n  
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)\n  
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable\u00601.Enumerator.InitializeReader(Enumerator enumerator)\n  
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable\u00601.Enumerator.\u003C\u003Ec.\u003CMoveNext\u003Eb__19_0(DbContext _, Enumerator enumerator)\n  
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func\u00603 operation, Func\u00603 verifySucceeded)\n  
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable\u00601.Enumerator.MoveNext()\nClientConnectionId:038757a9-2247-477d-98c5-31eacfa8d39d\nError Number:-2,State:0,Class:11

我们还在服务器上运行了跟踪,我们可以看到登录发生,然后在一段时间后注销发生,在工作查询上,我们可以看到登录、查询,然后直接注销.

下面是一个由其中一个断开的查询生成的SQL示例,它是一个连接,但在99.99%的情况下,结果总是1比1

SELECT [t0].[id],
       [t0].[accountid],
       [t0].[amendedondateutc],
       [t0].[billrundate],
       [t0].[billingdate],
       [t0].[contractid],
       [t0].[corrolationid],
       [t0].[createdondateutc],
       [t0].[creditnotenumber],
       [t0].[deletedflag],
       [t0].[description],
       [t0].[invoicenumber],
       [t0].[invoicesent],
       [t0].[methodid],
       [t0].[ordernumber],
       [t0].[paid],
       [t0].[partyid],
       [t0].[status],
       [t0].[tenantid],
       [t0].[tokenid],
       [t1].[id],
       [t1].[amendedondateutc],
       [t1].[createdondateutc],
       [t1].[deletedflag],
       [t1].[description],
       [t1].[externalitemid],
       [t1].[invoicedescription],
       [t1].[note],
       [t1].[priceexcluding],
       [t1].[quantity],
       [t1].[transactiondate],
       [t1].[transactionid]
FROM   (SELECT TOP(1) [t].[id],
                      [t].[accountid],
                      [t].[amendedondateutc],
                      [t].[billrundate],
                      [t].[billingdate],
                      [t].[contractid],
                      [t].[corrolationid],
                      [t].[createdondateutc],
                      [t].[creditnotenumber],
                      [t].[deletedflag],
                      [t].[description],
                      [t].[invoicenumber],
                      [t].[invoicesent],
                      [t].[methodid],
                      [t].[ordernumber],
                      [t].[paid],
                      [t].[partyid],
                      [t].[status],
                      [t].[tenantid],
                      [t].[tokenid]
        FROM   [dbo].[transactions] AS [t]
        WHERE  [t].[id] = '*****') AS [t0]
       LEFT JOIN [dbo].[transactionitems] AS [t1]
              ON [t0].[id] = [t1].[transactionid]
ORDER  BY [t0].[id] 

推荐答案

这个问题在GitHub的这个问题上得到了详细的讨论:https://github.com/dotnet/SqlClient/issues/647.

简而言之...这是由底层网络基础设施造成的.数据包在网络层被"截断",这导致SqlClient在新的数据包中重新提交未确认的字节,该新数据包再次被截断.该过程将继续,直到请求超时.

您可以通过降低连接字符串中的数据包大小来解决此问题.您可以通过在连接字符串后追加;Packet Size=512来完成此操作.512是此属性的最小大小,但您可以随意使用此值.

请注意,降低数据包大小会降低性能,因为这会增加客户端和服务器上的CPU使用率.

有关SqlConnection.PacketSize属性的更多信息,请参见https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnection.packetsize?view=dotnet-plat-ext-8.0.

.net相关问答推荐

竖线在 PropertyGroup .csproj 文件中的含义

通过 System.Net.Mail 的 VB SMTP 停止工作

C#.Net 中的可选返回

使用 Thread.Abort() 有什么问题

MongoDB C# 驱动程序 - 忽略绑定字段

.NET 应用程序的链接器状态(又名请先生,我可以有一个链接器2009 年版)

如何从 appsettings.json 中获取价值

使用 C# 清除文本文件的内容

HttpClient 和使用代理 - 不断得到 407

调用委托与方法的性能

如何从字符串中删除所有字母字符?

使用 C# 在 .NET 4.0 中 Task.Run 的替代方法是什么?

如何对无法加载的 VSTO 插件进行故障排除?

覆盖方法上的 C# 可选参数

有没有办法从方法返回匿名类型?

如何仅在需要时提升权限?

如何在 WPF 中的 Xaml 文件中添加注释?

为什么要使用 C# 类 System.Random 而不是 System.Security.Cryptography.RandomNumberGenerator?

安装带有恢复操作的 Windows 服务以重新启动

WinForms 中的模型视图演示者