Health Check 检查来自 EF DB 的数据导致线程问题

Health Check checking data from EF DB causes threading issue

我正在使用 AspNetCore.HealthCheck nuget 包对 .net core 3.1 应用程序实施一组健康检查。一些健康检查必须到达 EFcore 数据库,以检查是否存在由其他系统更新的某些数据,以验证其他进程是否正确 运行。

为此实施一次健康检查时一切正常,但一旦我实施第二次健康检查或多或少相同,但有一些变体,我在第一次调用时遇到线程问题EF 核心在下一个到达之前尚未完成。

存储库中的 EF 核心代码

public async Task<IEnumerable<EstateModel>> ListEstates(string customerId)
    {
        try
        {
            var estates = _productDbContext.Estates.AsNoTracking().Where(p => p.CustomerId == customerId)
                .Include(e => e.Meters)
                .ThenInclude(m => m.Counters)
                .Include(e => e.Installations);
            var entities = await estates.ToListAsync().ConfigureAwait(false);

            return _mapper.Map<List<EstateModel>>(entities);
        }
        catch (Exception ex)
        {
            Log.Error($"Error listing estate by customer: {customerId}", ex);
        }

        return null;
    }

健康检查示例

public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
    {
        var configs = new List<ConsumptionHealthCheckConfig>();
        _configuration.GetSection("HealthCheckSettings:GetConsumptionGas").Bind(configs);

        foreach (var config in configs)
        {
            try
            {
                return await _healthService.CheckConsumptionHealth(config, false, false);
            }
            catch(Exception ex)
            {
                return new HealthCheckResult(HealthStatus.Unhealthy, $"An error occurred while getting consumption for {config.Detailed.InstallationNumber} {ex}", ex);
            }
        }

        return new HealthCheckResult(HealthStatus.Healthy);
    }

healthservice方法

public async Task<HealthCheckResult> CheckConsumptionHealth(ConsumptionHealthCheckConfig config, bool isWater, bool isHeating)
    {
        if ((config.Detailed?.InstallationNumber ?? 0) != 0 && (config.Detailed?.MeterNumber ?? 0) != 0)
        {
            var estates = await _estateService.GetEstates(config.Detailed.CustomerNo);
Rest is omitted...

配置服务中的AddHealthChecks

internal static void Configure(IConfiguration configuration, IServiceCollection services)
    {
        services.AddHealthChecks()
            //Consumption
            .AddCheck<GetConsumptionElectricityHealthCheck>("Consumption Electricity", failureStatus: HealthStatus.Unhealthy, tags: new[] {"Consumption"})
            .AddCheck<GetConsumptionWaterHealthCheck>("Consumption Water", failureStatus: HealthStatus.Unhealthy, tags: new[] {"Consumption"})

我遇到的异常是

A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.

并且在查看所提供的 link 时,它指出我应该始终立即等待任何对数据库的调用,我们显然会这样做。

我曾尝试将 GetEstates 部分移至运行状况检查本身而不是我的服务,但随后我遇到了一个问题,即在配置数据库时尝试访问它。

因此,当这些消费健康检查同时到达 EF 核心时,我的问题就出现了,但我看不出如何避免这种情况发生,因为没有明显的选项可以告诉健康检查 运行 按顺序或者如果我实现了一个丑陋的屁股 Thread.Sleep 并且据我所知,应该没有必要在 EF Core 之上实现线程锁定或者我不正确吗?

任何帮助将不胜感激!

this issue 中所述,所有运行状况检查都使用相同的服务范围并并行进行 运行。我建议您在访问您的 DbContext.

的任何健康检查中创建一个新的服务范围
public virtual async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken))
{
    using var scope = serviceProvider.CreateScope();
    var healthService = scope.ServiceProvider.GetRequiredService<...>();
    ...
}