Whether during application startup, multi-step workflows, or monitoring an application’s overall health, verifying database connectivity is essential for smooth operation and graceful handling of potential failures. It improves reliability and the overall user experience.
In modern containerized environments, often managed by orchestrators like Kubernetes (K8S), it’s equally important for services to report their health status. This allows orchestrators to monitor and effectively manage containerized applications. Database health checks play an important role in this process.
In this article, we’ll explore two common and straightforward approaches for testing database availability with EF Core. First, the CanConnectAsync() method for application-level checks, and second, the DbContext check with ASP.NET Core health checks framework for infrastructure-level monitoring.
Let’s dive in!
Prerequisites for Running the Database Connectivity Checks
This article requires a running SQL Server database. To keep things simple and automated, we’ll use the Testcontainers NuGet package to programmatically create and manage a SQL Server instance in a Docker container.
After configuring a SQL Server Test Container, we then need to configure our ApplicationDbContext
to connect to this SQL Server instance:
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString), contextLifetime: ServiceLifetime.Scoped);
With our setup complete, we’re ready to explore how to test database connectivity.
Let’s begin!
Using CanConnectAsync for Database Connectivity Checks
Sometimes it is important for our application to determine whether it can obtain a database connection prior to attempting an execution of a specific task. This is particularly helpful for processes that span multiple requests. In other cases, periodic connectivity checks can enable the application to take preemptive actions to handle potential issues.
Let’s look at a few examples of when checking the connection state can be useful:
- In a multi-step process that relies on repeated database interactions, it’s more efficient to verify connectivity upfront than to fail halfway due to an unavailable database
- Applications that can run offline with limited features and notify the user
- Applications that can switch to backup infrastructure when the main one is unavailable
- Monitoring and alerting database connectivity regularly can generate alerts for the operations team, enabling them to react swiftly and reduce downtime.
Luckily, EF Core provides a built-in method: CanConnectAsync()
. This method checks if the database connection is valid and open, enabling developers to handle potential connectivity issues. It works by attempting to execute a SELECT 1
query in the database. If the attempt is successful, it returns true
; if it fails, it returns false
.
To demonstrate this we’ll create a minimal API that internally uses the CanConnectAsync()
method:
app.MapGet("/can-connect", async (ApplicationDbContext applicationDbContext, CancellationToken cancellationToken) => { var canConnect = await applicationDbContext.Database.CanConnectAsync(cancellationToken); return canConnect ? Results.Ok("Connected successfully") : Results.Problem("Cannot connect", statusCode: StatusCodes.Status503ServiceUnavailable); });
Our API returns a result based on the outcome of the connection attempt. If the connection attempt is successful the response will be Connected successfully
with the status code 200 OK
. Otherwise, we’d get Cannot connect
and a status code 503 Service Unavailable
.
To test our minimal API, let’s launch our application and send a GET request to /can-connect
.
If our Docker database container is up and running, as expected we will get Connected successfully
. On the other hand, if we pause or stop the container the result will be Service Unavailable
.
This straightforward method helps us determine our database’s availability at runtime, enabling the application to handle connectivity issues gracefully.
Using HealthChecks for Infrastructure-Level Checks and Container Orchestrators
Modern software architecture often breaks down complex software into multiple applications. These applications typically interact with each other within a private network. As such, it’s important that they report their health status to other components or orchestrators, such as Kubernetes.
In such scenarios, health checks are critical, because container orchestrators rely on them to determine whether to route traffic to an instance. If an instance cannot operate due to database connectivity issues, the orchestrator can disallow traffic to it, and reroute it to healthy instances.
ASP.NET Core provides a complete and robust health check framework that integrates seamlessly with EF Core, making it ideal for monitoring database connectivity.
To register a health check for our DbContext we first need to add the HealthChecks NuGet package:
dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
Having the package installed, we can then register the health check to our service collection with AddDbContextCheck<ApplicationDbContext>()
:
builder.Services.AddHealthChecks() .AddDbContextCheck<ApplicationDbContext>("Database Health", failureStatus: HealthStatus.Unhealthy);
Here, we name the health check Database Health
and configure it to report an Unhealthy status if the connection attempt fails. Of course, this is fully customizable.
In order to access our health checks, we define the health check endpoint in our application’s startup configuration in the Program
class:
app.MapHealthChecks("/health");
Once everything is set up, we can test the health check by sending a request to the /health
endpoint.
As before, if our database is operational, we’ll receive a 200
Healthy
response; otherwise, it will return 503 Unhealthy
status. Even though this approach looks pretty simple, it’s actually very powerful. It allows container orchestrators to monitor applications and manage them effectively based on their health status.
Conclusion
In this article, we explored two practical approaches for testing database connectivity using EF Core. If your applications frequently encounter database unavailability, subsequent crashes, or unpredictable behavior, perhaps what we’ve learned today could prove valuable. Stay tuned for our next article!