Background services and startup tasks
Use standard .NET hosted services for application initialization and background work. Orleans participates in the same Generic Host, so registration order can ensure Orleans is ready before a hosted service starts.
Using BackgroundService (Recommended)
Section titled “Using BackgroundService (Recommended)”Register Orleans first, then the BackgroundService:
public static async Task RunBackgroundService(string[] args){ var builder = Host.CreateApplicationBuilder(args);
builder.UseOrleans(siloBuilder => { // Configure Orleans. });
builder.Services.AddHostedService<GrainPingService>();
await builder.Build().RunAsync();}public sealed class GrainPingService : BackgroundService{ private readonly IGrainFactory _grainFactory; private readonly ILogger<GrainPingService> _logger;
public GrainPingService( IGrainFactory grainFactory, ILogger<GrainPingService> logger) { _grainFactory = grainFactory; _logger = logger; }
protected override async Task ExecuteAsync( CancellationToken stoppingToken) { using var timer = new PeriodicTimer(TimeSpan.FromSeconds(30)); var grain = _grainFactory.GetGrain<IHealthGrain>("background-check");
while (await timer.WaitForNextTickAsync(stoppingToken)) { try { await grain.Ping(); } catch (Exception exception) when (exception is not OperationCanceledException) { _logger.LogError(exception, "Grain ping failed"); } } }}Honor stoppingToken so the service doesn’t delay silo shutdown. Use IHostedService directly for one-time startup and shutdown work.
Orleans startup tasks
Section titled “Orleans startup tasks”Register a delegate
Section titled “Register a delegate”Use AddStartupTask when initialization must complete at an Orleans lifecycle stage before startup can continue:
public static void RegisterStartupTask(ISiloBuilder siloBuilder){ siloBuilder.AddStartupTask( async (services, cancellationToken) => { var grainFactory = services.GetRequiredService<IGrainFactory>(); var grain = grainFactory.GetGrain<IInitializerGrain>("application"); await grain.Initialize(cancellationToken); }, ServiceLifecycleStage.Active);}The default stage is Active. An exception fails silo startup. This is appropriate for mandatory validation or initialization, but not for optional work that can retry after the host becomes ready.
Register an IStartupTask implementation
Section titled “Register an IStartupTask implementation”For reusable tasks, implement IStartupTask:
public sealed class ValidateDependenciesTask : IStartupTask{ private readonly IDependencyValidator _validator;
public ValidateDependenciesTask(IDependencyValidator validator) { _validator = validator; }
public Task Execute(CancellationToken cancellationToken) => _validator.ValidateAsync(cancellationToken);}public static void RegisterValidateDependenciesTask( ISiloBuilder siloBuilder){ siloBuilder.AddStartupTask<ValidateDependenciesTask>( ServiceLifecycleStage.ApplicationServices);}Choose the right mechanism
Section titled “Choose the right mechanism”| Requirement | Mechanism |
|---|---|
| Continuous loop or scheduled application work | BackgroundService |
| One-time host startup and shutdown work | IHostedService |
| Mandatory initialization at a specific Orleans stage | AddStartupTask |
| Start and stop callbacks integrated with an Orleans subsystem | ILifecycleParticipant<T> with ISiloLifecycle |
Don’t use a startup task for long-running loops, database migrations that multiple replicas could race to apply, or work that needs unbounded retries. Coordinate migrations externally or make them safely single-writer.
See Orleans silo lifecycle for lifecycle stage selection.
