Never forget an EF Core migration anymore, rely on webapp HealthChecks
Sounds familiar ?
It’s Friday afternoon, you’ve accepted to handle your team’s weekly delivery. It has to be up by Monday and no one else is available.
Although you know you shouldn’t deliver on a Friday, you’re confident, you have released this app a million times, piece of cake :
- Package delivered in Azure DevOps ✔️
- Swagger is up, webapp has started ✔️
- Yummy cookie eaten ✔️
Few hours later, production goes crazy, 500 errors everywhere, datacenter is on 🔥 🔥 🔥.
Too bad, you were having drinks with your friends, you’re running back home.
Ultimately, CSI (or AppInsights logs, it depends) will unveil the awful truth, you forgot to run an EntityFramework migration 💔
You hate yourself, you broke production, and most of all, you wasted a great Belgian 🍺.
Wait, can’t EF run required migrations on its own ?
Absolutely, EF can lookup for missing migrations and run them on the fly, during application startup.
However, MSDN advises you not to use this feature on production Database. Sometimes, a good old human supervised manual operation is better.
Oops, did I say manual operation ?
Quick words about webapps health checking
As part of .NET Core SDK, Microsoft has delivered an amazing healthcheck runtime.
Once it’s setup in your Startup class, your app will host a set of health check endpoints that you can query to assert its health.
Out of the box, it can help you ensure that all your usual dependencies are up and running, thanks to its extensible design and the hyper active .NET Core dev community :
- Other webapps : just call their own health — or ping — endpoint
- Databases : AzureSql, CosmosDB, ElasticSearch
- Messaging middlewares : Azure Service Bus, RabbitMQ
- And so on…
It even comes with a very convenient self hosted website, HealthChecks.UI, to display the health check results :
Leverage on this SDK to check EntityFramework migrations
We published on NuGet a custom health check that will ensure all migrations were executed, for a given Entity Framework Core DbContext.
It will work for any storage backend (AzureSQL, Oracle, CosmosDB SQL API, SQLite, etc), thanks to EF Core abstractions.
1- Follow this tutorial to setup the HealthCheck runtime in your webapp
2- Make sure you add Younited.HealthCheck.EntityFrameworkMigrations nuget to your project :
3- Register the EntityFrameworkMigrationsHealthCheck into the pipeline, using the extension method shown below. You will have to provide your custom DbContext :
That’s all folks ! 🎁 🎁 🎁
Now, querying the detailed health endpoint of your api will provide you with the list of migrations that could not be found on the DB, if any
HealthCheck.UI will also report the failure, obviously :
❗️ ❗️ ❗️ Note that this check is designed to stop searching for missing migrations once it confirmed they were all there. It’s meant to avoid paying for the lookup every time the health check is invoked. We assume that if all migrations were there at some point, they won’t be rolled back. Of course, after the next process startup, lookup will happen again until it confirms no migrations are missing. ❗️ ❗️ ❗️
However, in case you need to alter this behavior, feel free to publish a PR on our repo 😉
For a complete sample app, you can have a look at the project repo.
How to include healthchecks in your CD process
You surely don’t want to add a manual step in your delivery process, to query the health endpoint.
Instead, you can ask your delivery pipeline to query the detailed health endpoint right after the deployment, and make sure it returns a 200 status code.
All delivery systems have scripting tasks (python, powershell, etc.) that you can leverage on to perform this api call.
Combined with the warmup slot mechanism of Azure, you can define a production delivery workflow as follow :
- Deliver on the production warmup slot
- Query the health check endpoint, assert that it returns 200
- Switch slots to finalize the deployment only if the healthcheck was a success
This way, in case the health check identifies a missing migration, or any other concern, you will have caught it before the actual delivery ✔️
Curious on how to write such custom health check ?
All code, including the sample app, is located on GitHub
First, you’ll have to create a class that implements IHealthCheck interface, from Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions nuget. As you can see, the interface is as simple as it can be :
In our case, the core of the implementation is how to look for the missing migrations. Lucky for us 🙏, there is a built-in method for this within EntityFramework Core packages, GetPendingMigrationsAsync :
Then, all we have to do is to call this lookup method only when the last check found missing migrations.
It’s not as straightforward as it could be, since the healthcheck instance is stateless. We have to register another service into the DI framework, as a singleton, to provide the healthcheck with persistent state capabilities.
Ultimately, the healthcheck implementation looks like this :
May the migrations be with you