'(.Net Core) How to test connection to db at startup?
.Net 6.0, EF, Asp.Net Core
I'm trying to interrupt my web server's startup if the connection to the db could not be established. I'm thinking that the natural place to insert the test is at the end of the Configure method in startup.cs.
I've found a lot of questions about that in stackoverflow but most of the time they don't address my situation :
- Some of them are about Unit tests, where the services are easy to instantiate/mock and there isn't a large Configuration sequence.
- Some of them simply instantiate the database but it's in simpler scenarios without the "Unit of Work" design pattern, which I use. It adds extra levels of wrapping and DI scoping (repositories/DbSets inside DbContext)
I've been trying to address it this way :
- I've exposed the "CanConnect" method that comes in standard in DbContext.Database. It's now callable once you have an instance of my DbContext unit of work object.
- Once I'm sure that the dbcontext is properly configured in ConfigureServices, I simply want to call this "CanConnect" at the end of Configure in Startup.cs.
But to do so, I need to get an instance of the DbContext first. I tried to do a quick "local" dependency injection the usual way :
var dbcontext = app.ApplicationServices.GetRequiredService<IDbContext>();
Problem : this fails at runtime because of the scope of Dbcontext. Apparently, only singletons can be injected in method "Configure". It's too early in the application's lifecyle to instantiate other scopes.
Out of desperation, I tried to create a Singleton service just for that, which in turn would receive the IDbContext in its contructor. Same issue : .Net's validation round detects that there's something fishy with the pyramid of scopes and throws an Exception.
So how do you get one instance of your DbContext as early as startup, or more generally how do you test the connection to your database as early as startup?
Solution 1:[1]
more generally how do you test the connection to your database as early as startup?
It's really simple: don't use DI. Create an instance of your DbContext using a constructor that takes a connection string.
Solution 2:[2]
People said "Don't use DI" but then it's a nightmare to get the configuration manually (with all the potential providers and so on) instead of letting DI providing it; DI was invented for a reason!
The way I did it:
//IMPORTANT: Absolutely do not do that if you work with Singletons! That would create copies of them.
public void ConfigureServices(IServiceCollection services) {
//... all the configuration
// At the very end :
using (var intermediateServiceProvider = services.BuildServiceProvider())
{
OnServicesConfigured(intermediateServiceProvider, this.config);
}
}
private static void OnServicesConfigured(ServiceProvider serviceProvider, IConfiguration config) {
using (var db = serviceProvider.GetRequiredService<MyDbContext>()) {
if (db.CanConnect()) { //CanConnect can be exposed in most classes inheriting DbContext
var log = serviceProvider.GetRequiredService<ILogging>();
log?.Information("Connection successful.");
return;
}
throw new Exception("Could not connect to database.");
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Joel Coehoorn |
Solution 2 | jeancallisti |