Как проверить работоспособность службы, которая отправляет http-запросы, используя конфигурацию базы данных в реализации IHealthCheck?

У меня есть 2 службы, которые делают запросы Http с базовым URL-адресом и аутентификацией, предоставляемой таблицей конфигурации в DbContext.

Я хочу реализовать проверку работоспособности, чтобы убедиться, что вызов службы успешен или нет, и у меня есть код ниже, например:

namespace Company.Api.Infrastructure.HealthChecks
{
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Diagnostics.HealthChecks;
    using Services;
    using System;
    using System.Threading;
    using System.Threading.Tasks;

    public class ExampleServiceHealthCheck : IHealthCheck
    {
        private readonly Guid _sampleOrganizationId = new Guid("8f3a6dd9-6146-4cfa-8ae8-a0fa09998e54");
        private readonly IHttpContextAccessor _httpContextAccessor;

        public ExampleServiceHealthCheck(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
        {
            // Trying to create a scope for the service so I don't get concurrent dbContext access exception.
            var exampleService = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService<ExampleService >();
            var invoices = await exampleService.GetInvoicesAsync(_sampleOrganizationId, false);
            if (invoices == null)
            {
                return HealthCheckResult.Unhealthy("Example service didn't return any invoices.");
            }

            return HealthCheckResult.Healthy("Successfully tested Example service with invoices call.");
        }
    }
}

Использование одной службы — это нормально, но когда я добавляю две службы, каждая из которых извлекает свою конфигурацию из базы данных, я получаю эту ошибку в одной из них случайным образом.

{
  "status": "Unhealthy",
  "results": {
    "DbContext": {
      "status": "Healthy",
      "description": null,
      "data": {}
    },
    "ExampleService": {
      "status": "Healthy",
      "description": "Successfully tested ExampleService with invoices call.",
      "data": {}
    },
    "ExampleService2": {
      "status": "Unhealthy",
      "description": "An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside \u0027OnConfiguring\u0027 since it is still being configured at this point. This can happen if a second operation is started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.",
      "data": {}
    }
  }
}

Вот как выглядит моя регистрация Healthcheck:

public static IServiceCollection AddApplicationHealthChecks(this IServiceCollection services)
{
    services.AddHealthChecks()
        .AddDbContextCheck<DbContext>()
        .AddCheck<ExampleServiceHealthCheck>("ExampleService")
        .AddCheck<ExampleService2HealthCheck>("ExampleService2");

    return services;
}

Как я могу пройти эту ошибку?


person BigThinker    schedule 04.12.2020    source источник


Ответы (1)


Я думаю, что исправил это, создав область перед доступом к службе следующим образом: я думаю, что это гарантирует, что службы обращаются к базе данных последовательно, а не параллельно, вызывая ошибку выше, и по умолчанию метод CreateScope создает область, если она отсутствует в места вне запросов.

namespace Company.Api.Infrastructure.HealthChecks
{
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Diagnostics.HealthChecks;
    using Services;
    using System.Threading;
    using System.Threading.Tasks;

    public class ExampleServiceHealthCheck : IHealthCheck
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        public ExampleServiceHealthCheck(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
        {
            using var serviceScope = _httpContextAccessor.HttpContext.RequestServices.CreateScope(); // <-- This line
            IExampleService exampleService = serviceScope.ServiceProvider.GetRequiredService<IExampleService>();
            var invoices = await exampleService.GetInvoicesAsync();
            if (invoices == null)
            {
                return HealthCheckResult.Unhealthy("Example service didn't return any invoices.");
            }

            return HealthCheckResult.Healthy("Successfully tested Example service with invoices call.");
        }
    }
}
person BigThinker    schedule 04.12.2020