gpt4 book ai didi

c# - 在 ServiceProvider 中使用时,如何防止 DbContext 用完可用的池连接?

转载 作者:行者123 更新时间:2023-12-03 04:57:53 25 4
gpt4 key购买 nike

我编写了一个 aspnet core 控制台应用程序,该应用程序将 Entity Framework Core 与 SQL Server 结合使用。该应用程序有多个 Azure 服务总线队列客户端,我假设每个客户端都在自己的线程中运行消息接收器处理程序 - 因此我相信它是第三方服务总线库中的线程应用程序。

为了避免在每个服务总线队列客户端中配置和创建 SQL 连接,我将依赖项注入(inject)与 Microsoft.Extensions.DependencyInjection.ServiceCollection 结合使用。然后,我将生成的 ServiceProvider 传递到每个服务总线队列客户端。

我的问题是,随着时间的推移,我的程序将停止工作,因为 SQL 连接池耗尽了可用连接。这就像内存泄漏,但它是 SQL 连接泄漏,因为我认为连接永远不会释放回池中。

我对此进行了一些阅读,据我了解,当连接超出范围时,它应该返回到池中。我还了解到线程可能会导致这些问题,这就是我提出服务总线队列客户端的原因,并且我相信消息处理程序是在线程内运行的。

我确信这是我忽略的简单问题,但在尝试了几种不同的方法后我无法修复它。我将在下面粘贴我的代码,然后解释我尝试过的一些事情。

因此,这是配置和创建 ServiceProvider 的代码...

private ServiceProvider BuildServiceProvider()
{
var services = new ServiceCollection();

services.AddDbContext<WibWorkspaceContext>(ConfigureDbContext, ServiceLifetime.Transient);

return services.BuildServiceProvider();
}

然后这是上面代码使用的 ConfigureDbContext 方法...

private void ConfigureDbContext(DbContextOptionsBuilder options)
{
var migrationsAssembly = typeof(WibWorkspaceContext).GetTypeInfo().Assembly.GetName().Name;
var sqlConnectionString = ConfigurationManager.ConnectionStrings["Sql"];

options
.UseLazyLoadingProxies()
.UseSqlServer(sqlConnectionString.ConnectionString, x => x.MigrationsAssembly(migrationsAssembly));
}

然后,我将该 ServiceProvider 传递到每个服务总线消息处理程序中,并在其中调用 GetRequiredService。因此,我确实相信对 GetRequiredService 的调用发生在服务总线客户端创建的线程内。下面是一些代码片段...

private async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
var sql = m_services.GetRequiredService<WibWorkspaceContext>();

...
}

ProcessMessagesAsync中,我使用了DbContext,但我从不做任何与处置它或任何事情相关的事情。我只是允许它超出范围,仅此而已。

这就是我的代码。该程序将正常运行,直到它开始耗尽 SQL 连接。此时它开始抛出以下错误和堆栈跟踪......

2020-10-02 23:07:55.1660 ERROR Timeout expired. The timeout periodelapsed prior to obtaining a connection from the pool. This may haveoccurred because all pooled connections were in use and max pool sizewas reached.

2020-10-02 23:07:15.3764 ERROR System.InvalidOperationException: Timeout expired.  The timeout period elapsed prior to obtaining a connection from the pool.  This may have occurred because all pooled connections were in use and max pool size was reached.
at Microsoft.Data.Common.ADP.ExceptionWithStackTrace(Exception e)
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at WhatInBoxWorkerService.Queues.CreateRelativityPackageQueueClient.ProcessMessagesAsync(Message message, CancellationToken token) in C:\Users\rcole\Projects\WhatInBox\src\Applications\WhatInBoxWorkerService\Queues\CreateRelativityPackageQueueClient.cs:line 203
at Microsoft.Azure.ServiceBus.MessageReceivePump.MessageDispatchTask(Message message)

正如您所料,程序开始崩溃,因为无法从连接池中检索到任何连接。

我尝试过的一件事是使用 AddDbContextPool 而不是 AddDbContext

我是否忘记做任何事情?

最佳答案

我怀疑您的依赖项注入(inject)设置存在问题,如下所示:

var sql = m_services.GetRequiredService<WibWorkspaceContext>();

如果m_services是您从未处置并基于您的评论的根容器

Within ProcessMessagesAsync I make use of the DbContext but I never do anything relating to disposing it or anything. I just allow it to go out of scope and that's it.

那么您最终可能会遇到所有 WibWorkspaceContext 永远不会被释放的情况。

为什么?

我假设您使用 Microsoft 扩展 DI。 transient /作用域服务仅在其作用域结束时才会被释放。如果您从根容器解析它们,则在容器被处置之前它们不会被处置。这就是为什么resolving transient IDisposable from root container isn't recommended .

要解决此问题,您可能需要自定义范围:

using(var scope = m_services.CreateScope())
{
var sql = scope.ServiceProvider.GetRequiredService<WibWorkspaceContext>();

} // <--- Your DB context will be disposed here

关于c# - 在 ServiceProvider 中使用时,如何防止 DbContext 用完可用的池连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64180433/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com