Entity framework 使用(托管)后台服务时.NET Core/EF Core(SQL)最大池问题
我和我的团队在一个高利用率的.NET Core Web应用程序上遇到了一些EF Core/SQL池问题Entity framework 使用(托管)后台服务时.NET Core/EF Core(SQL)最大池问题,entity-framework,asp.net-core,.net-core,entity-framework-core,ef-core-3.0,Entity Framework,Asp.net Core,.net Core,Entity Framework Core,Ef Core 3.0,我和我的团队在一个高利用率的.NET Core Web应用程序上遇到了一些EF Core/SQL池问题 超时已过期。从池中获取连接之前经过的超时时间。发生这种情况的原因可能是所有池连接都在使用中,并且达到了最大池大小。 在托管服务(QueuedHostedService)的后台任务中引用EF Core/ApplicationDbContext时,问题开始出现。以下是指南: 我遵循了推荐的依赖项注入和托管服务指南 复制步骤 相关线路: Startup.cs 讨论 在mysingletonatad
超时已过期。从池中获取连接之前经过的超时时间。发生这种情况的原因可能是所有池连接都在使用中,并且达到了最大池大小。
在托管服务(QueuedHostedService)的后台任务中引用EF Core/ApplicationDbContext时,问题开始出现。以下是指南:
我遵循了推荐的依赖项注入和托管服务指南
复制步骤
相关线路:
Startup.cs
讨论
在mysingletonataddstobackground内部,在使用(var scope)
完成后,是否应该释放作用域,然后释放作用域worker
(scoped),然后释放应用程序dbconext
(scoped),然后关闭连接/池连接
是否有我没有正确实现的东西导致连接池泄漏
进一步的技术细节
EF核心版本:3.1.4
数据库提供程序:Microsoft.EntityFrameworkCore.SqlServer
目标框架:.NET Core 3.1.4
操作系统:Windows Server 2016、SQL Server 2016
IDE:16.6您使用的文章包含一个示例,而不是推荐的工作方式。该队列中的“作业”是如何处理的?一次一个?他们中有许多人同时参加?您的
ExecuteAsync
做什么?您真的需要一个作业队列,还是可以将消息发布到队列中,让ExecuteAsync
逐个处理它们?如果您的后台服务只执行一个作业,那么没有理由认为作业队列很复杂。“作业队列”的问题是一半的处理代码存在于您的单例中,另一半存在于后台服务中。对其中一个进行更改,会影响另一个的工作方式。问题几乎可以肯定是在你没有发布的代码中。这个问题已经提供给EF核心问题跟踪者-。他们在提供的代码中没有发现任何可能导致问题的原因,我也是。因为问题显然是由未知代码引起的,这里没有显示,所以问题无法回答。您使用的文章包含一个示例,而不是推荐的工作方式。该队列中的“作业”是如何处理的?一次一个?他们中有许多人同时参加?您的ExecuteAsync
做什么?您真的需要一个作业队列,还是可以将消息发布到队列中,让ExecuteAsync
逐个处理它们?如果您的后台服务只执行一个作业,那么没有理由认为作业队列很复杂。“作业队列”的问题是一半的处理代码存在于您的单例中,另一半存在于后台服务中。对其中一个进行更改,会影响另一个的工作方式。问题几乎可以肯定是在你没有发布的代码中。这个问题已经提供给EF核心问题跟踪者-。他们在提供的代码中没有发现任何可能导致问题的东西,我也是。因为问题显然是由未知代码引起的,这里没有显示,所以问题无法回答。
...
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
...
services.AddScoped<IScopedWorker, ScopedWorker>();
services.AddSingleton<MySingletonThatAddsToBackground>();
// Follow the host service guide from microsoft.
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
public class MySingletonThatAddsToBackground
{
private readonly IBackgroundTaskQueue _taskQueue;
private readonly ILogger _logger;
private readonly CancellationToken _cancellationToken;
public IServiceProvider _services { get; }
public MySingletonThatAddsToBackground(IServiceProvider services, IBackgroundTaskQueue taskQueue,
ILogger<MonitorLoop> logger,
IHostApplicationLifetime applicationLifetime)
{
_services = services;
_taskQueue = taskQueue;
_logger = logger;
_cancellationToken = applicationLifetime.ApplicationStopping;
}
public void DoWorkBackground()
{
// Enqueue a background work item
_taskQueue.QueueBackgroundWorkItem(async token =>
{
try
{
using (var scope = _services.CreateScope())
{
var scopedWorker = scope.ServiceProvider.GetRequiredService<IScopedWorker>();
await scopedWorker.DoWork();
}
}
catch (OperationCanceledException)
{
// Prevent throwing if the Delay is cancelled
}
});
}
}
public class ScopedWorker : IScopedWorker
{
private readonly ApplicationDbContext _db;
public ScopedWorker(ApplicationDbContext db)
{
_db = db;
}
public void DoWork()
{
var customers = _db.MyCustomers.ToListAsync();
// Do stuff to customers.
await _db.SaveChangesAsync();
}
}