Introduction
Learn how to warm up Microsoft EF Core for ASP.NET Core projects when a project has many models with complex configurations.
This is done using a hosted service that warms up Entity Framework Core (EF Core) by preloading the model and executing a lightweight query. This service ensures that EF Core's query compilation and model initialization occur during application startup, reducing latency for the first database operation at runtime.
Recommendations
As presented in the source code, log errors for failures and, when dealing with databases that may take time to connect, set a timeout with the provided CancellationToken.
Configuration
Add the following two classes, which provide access to settings in appsettings.json for when a CancellationToken timeout is needed in the hosted service.
public sealed class AppConfiguration
{
private static readonly Lazy<AppConfiguration> _instance = new(() => new AppConfiguration());
public static AppConfiguration Instance => _instance.Value;
public bool Use { get; }
public int Timeout { get; }
private AppConfiguration()
{
var configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
.Build();
var settings = configuration
.GetSection(UseCancellationTokenTimedSettings.SectionName)
.Get<UseCancellationTokenTimedSettings>();
Use = settings!.Use;
Timeout = settings.Timeout;
}
}
public sealed class CancellationTokenSettings
{
public const string SectionName = "CancellationTokenSettings";
public bool Use { get; init; }
public int Timeout { get; init; } // milliseconds
}
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=FluentValidation1;Integrated Security=True"
},
"UseCancellationTokenTimed": {
"Use": false,
"Timeout": 3000
}
}
Add this class which is used to warm-up EF Core
- Replace Context for GetRequiredService with your DbContext
- Replace Person used for AnyAsync with any model in your DbContext
- Log.Error is from Serilog, change this if not using Serilog
using Microsoft.EntityFrameworkCore;
using Serilog;
public class EntityCoreWarmupService(IServiceProvider serviceProvider) : IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
if (AppConfiguration.Instance.Use)
{
var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(AppConfiguration.Instance.Timeout));
cancellationToken = cts.Token;
}
try
{
using (var scope = serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<Context>();
_ = context.Model;
// Execute a simple, lightweight query to warm up query compilation
await context.Person.Take(1).AnyAsync(cancellationToken);
}
await Task.CompletedTask;
}
catch (Exception exception)
{
Log.Error(exception, "EF Core warmup failed");
}
}
}
In Program.cs, add the following after adding your dbContext service.
- Validate appsettings.json for CancellationToken in the warm-up service
- Using dependency injection add the warm-up service
builder.Services
.AddOptions<UseCancellationTokenTimedSettings>()
.Bind(builder.Configuration.GetSection(UseCancellationTokenTimedSettings.SectionName))
.Validate(s => s.Timeout >= 0, "Timeout must be >= 0")
.ValidateOnStart();
builder.Services.AddHostedService<EntityCoreWarmupService>();
var app = builder.Build();
Source code
- Under MSSQLLocalDB create the following database FluentValidation1 best done with SSMS.
- Finish by creating table and populating tables with populate.sql under DatabaseScripts folder in the sample project.
🛑 Important
The sample code uses a single table to keep it simple to follow along with the instructions, while the presented service is meant for use with multiple tables.
Summary
The presented hosted service will warm up database code at the start of an ASP.NET Core web application, rather than when a user needs to interact with data. Take time regarding testing for timeouts and ensure errors are logged.
Top comments (0)