Amila
Amila

Reputation: 3816

Accessing IOptions and DbContext within ConfigureServices

I've started to implement health checks in my .NET Core Web API. There are two health checks, one for checking if the SQL Server has any pending migrations and the other is checking if another API is live. Both added within ConfigureServices in Startup class.

In order to do the migration check, I need to access the DbContext which has already been added to DI using AddDbContext and to check the API, I need to get the API base url from configuration which is already in DI using services.Configure<>. I use the following code to get access to the DbContext.

I'm using AspNetCore.HealthChecks.Uris package to use AddUrlGroup health check.

var sp = services.BuildServiceProvider();
var dbContext = sp.GetService<AppDbContext>();
var apis = sp.GetService<IOptions<InternalServicesConfiguration>>().Value;

services.AddHealthChecks().AddCheck("Database", new SqlDatabaseHealthCheck(dbContext), tags: new[] { "ready" })
            .AddUrlGroup(new Uri(new Uri(apis.Api1BaseUri), "/health/live"), HttpMethod.Get, "API 1", HealthStatus.UnHealthy, new []{"ready"});

But services.BuildServiceProvider() shows the following warning:

Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'

I can get the api base urls using

_configuration.GetSection("InternalServicesConfiguration").Get(typeof(InternalServicesConfiguration));

But I can't think of an alternative way to access the DbContext.

Any help much appreciated.

Upvotes: 1

Views: 1091

Answers (3)

Jakub Kozera
Jakub Kozera

Reputation: 3473

With regard to accessing IOptions (please see updated health check). Is there another way other than getting it using _configuration.GetSection("").Get<>()

You could register those options in a following way (in ConfigureServices):

services.Configure<InternalServicesConfiguration>(Configuration.GetSection("InternalServicesConfiguration"));

And to get those options, in your class just inject IOptions<InternalServicesConfiguration> options, where the options.Value prop is the configuration value

Upvotes: 0

Jakub Kozera
Jakub Kozera

Reputation: 3473

You can register your healthcheck like this:

services.AddHealthChecks()
    .AddCheck<ExampleHealthCheck>("Database");

And then just inject your DbContext into ExampleHealthCheck class, which has to implement IHealthCheck interface

Upvotes: 2

Sotiris Panopoulos
Sotiris Panopoulos

Reputation: 1613

There are some healthchecks you can use directly for EF in the official docs But if you want to write any custom or more complex checks, your best bet might be to create a class that implements the IHealthCheck interface, where you can inject anything you want.

Also from the docs about Custom health checks, an example:

public class ExampleHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var healthCheckResultHealthy = true;

        if (healthCheckResultHealthy)
        {
            return Task.FromResult(
                HealthCheckResult.Healthy("A healthy result."));
        }

        return Task.FromResult(
            HealthCheckResult.Unhealthy("An unhealthy result."));
    }
}

which, as kebek alerady answered, you will register like

services.AddHealthChecks()
    .AddCheck<ExampleHealthCheck>("example_health_check");

Upvotes: 0

Related Questions