52

Reading over the documentation for ASP.NET Core, there are two methods singled out for Startup: Configure and ConfigureServices.

Neither of these seemed like a good place to put custom code that I would like to run at startup. Perhaps I want to add a custom field to my DB if it doesn't exist, check for a specific file, seed some data into my database, etc. Code that I want to run once, just at app start.

Is there a preferred/recommended approach for going about doing this?

Jared Moore
  • 3,765
  • 26
  • 31
Kyle B.
  • 5,737
  • 6
  • 39
  • 57
  • Adding this comment as I felt it assisted in keeping the Startup.cs file clean for the accepted solution: http://odetocode.com/blogs/scott/archive/2016/08/30/keeping-a-clean-startup-cs-in-asp-net-core.aspx – Kyle B. Aug 30 '16 at 17:23
  • 2
    I know this was asked in 2016 but the same problem still applies in 2018, you should consider the points I've put and reconsider the accepted answer. – Professor of programming Jan 03 '18 at 17:45

3 Answers3

30

I agree with the OP.

My scenario is that I want to register a microservice with a service registry but have no way of knowing what the endpoint is until the microservice is running.

I feel that both the Configure and ConfigureServices methods are not ideal because neither were designed to carry out this kind of processing.

Another scenario would be wanting to warm up the caches, which again is something we might want to do.

There are several alternatives to the accepted answer:

  • Create another application which carries out the updates outside of your website, such as a deployment tool, which applies the database updates programmatically before starting the website

  • In your Startup class, use a static constructor to ensure the website is ready to be started

Update

The best thing to do in my opinion is to use the IApplicationLifetime interface like so:

public class Startup
{
    public void Configure(IApplicationLifetime lifetime)
    {
        lifetime.ApplicationStarted.Register(OnApplicationStarted);
    }

    public void OnApplicationStarted()
    {
        // Carry out your initialisation.
    }
}
Professor of programming
  • 2,978
  • 3
  • 30
  • 48
  • 6
    I discovered that a request processing has been already started (it has started executing a middleware) while the OnApplicationStarted method has not completed yet. In my case, it was starting a transaction while the database has not been updated yet. Also the ApplicationStarted doc says: "Triggered when the application host has fully started and is about to wait for a graceful shutdown." which does not suggest it's not a place for some initialization stuff before request handling is started. – xhafan Sep 06 '18 at 06:59
  • 4
    Just one note, IApplicationLifetime is deprecated in .NET 3.x, the new interface now is called: IHostApplicationLifetime – Bruno Jul 08 '20 at 17:25
23

This can be done by creating an IHostedService implementation and registering it using IServiceCollection.AddHostedService<>() in ConfigureServices() in your startup class.

Example

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;

public class MyInitializer : IHostedService
{
    public Task StartAsync(CancellationToken cancellationToken)
    {
        // Do your startup work here

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        // We have to implement this method too, because it is in the interface

        return Task.CompletedTask;
    }
}
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHostedService<MyInitializer>();
    }
}

Notes

Zero3
  • 594
  • 2
  • 11
  • 18
  • 2
    In my opinion, that is the best approach When somebody would want to just "fire and forget" then you still can do it but still you are able to get services from DI. keep in mind that IServiceProvider what you can get is a main root, so if you want to get some scoped think, you need to create scope first – KondzioSSJ4 Mar 15 '22 at 19:45
15

Basically there are two entry points for such custom code at startup time.

1.) Main method

As a ASP.NET Core application has got the good old Main method as entry point you could place code before the ASP.NET Core startup stuff, like

public class Program
{
    public static void Main(string[] args)
    {
        // call custom startup logic here
        AppInitializer.Startup();

        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        host.Run();
    }
}

2.) Use your Startup class

As you already stated in your question is the Configure and ConfigureServices a good place for your custom code.

I would prefer the Startup class. From the runtime perspective it does not matter, if the call is called in startup or somewhere else before the host.Run() call. But from a programmer's point of view who is accustomed to the ASP.NET framework then his first look for such logic would be the Startup.cs file. All samples and templates put there the logic for Identity, Entity Framework initialization and so on. So as a convention I recommend to place the initialization stuff there.

Ralf Bönning
  • 14,515
  • 5
  • 49
  • 67
  • The `Startup` class also offers the benefit of the built dependency injection container `IServiceProvider` being available in `ConfigureServices` through the`IApplicationBuilder.ApplicationServices` property. – Brad Aug 20 '16 at 05:36
  • 6
    The OP said "Neither of these seemed like a good place to put custom code", not that they are a good place. – Professor of programming Jan 03 '18 at 17:41
  • With all the available methods of running side code in ASP.Net Core, the Main() method is not one of them. Consider IHostedService. – stricq Aug 15 '23 at 19:47