DEV Community

Cover image for ASP.NET Core warmup EF Core
Karen Payne
Karen Payne

Posted on

ASP.NET Core warmup EF Core

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
}
Enter fullscreen mode Exit fullscreen mode

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 
  }
}
Enter fullscreen mode Exit fullscreen mode

Add this class which is used to warm-up EF Core

  1. Replace Context for GetRequiredService with your DbContext
  2. Replace Person used for AnyAsync with any model in your DbContext
  3. 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");
        }

    }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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.

Source code

🛑 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)