Skip to main content

Hosting the runtime

You can embed the runtime into anything from a background worker to a full ASP.NET Core application. The examples highlight the patterns that matter in production.

Minimal console host

Use a console app when you want a single-process worker that owns orchestration execution. DurableFunctionDemo shows the bare essentials.

DurableFunctionDemo.cs
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = loggerFactory.CreateLogger<DurableFunctionRuntime>();

var stateStore = new InMemoryStateStore(); // swap for SQLite or PostgreSQL in production
var runtime = new DurableFunctionRuntime(stateStore, logger, loggerFactory: loggerFactory);

runtime.RegisterJsonFunction("SayHello", (_, input) =>
{
Console.WriteLine($"Hello, {input}!");
return Task.FromResult($"Greeted {input}");
});

await runtime.RunAndPollAsync(cts.Token); // polls storage and executes orchestrations

The polling loop blocks until the cancellation token is triggered. Wrap it inside a hosted service if you need lifetime management.

Dependency injection and hosted services

OrchestrationClientExample wires everything through the generic host so you can lean on configuration, logging, and health checks.

OrchestrationClientExample.cs
Host.CreateDefaultBuilder()
.ConfigureServices(services =>
{
services.AddSingleton<IStateStore>(_ => new InMemoryStateStore());

services.AddSingleton<DurableFunctionRuntime>(provider =>
{
var store = provider.GetRequiredService<IStateStore>();
var logger = provider.GetRequiredService<ILogger<DurableFunctionRuntime>>();

var runtime = new DurableFunctionRuntime(store, logger);
RegisterFunctions(runtime); // register orchestrators + activities once
return runtime;
});

services.AddDurableFunctionsManagementService(); // exposes IOrchestrationClient
});

Once registered, inject DurableFunctionRuntime to start the polling loop inside a background service, and IOrchestrationClient anywhere you need to trigger or query orchestrations.

Multi-host safety

When several processes share the same database you need coordination so the same instance does not run twice. The concurrency implementation combines optimistic concurrency with short-lived leases.

  • ConcurrentSqliteStateStore and ConcurrentPostgreSqlStateStore automatically add the required schema columns and perform compare-and-swap updates.
  • ConcurrentDurableFunctionRuntime renews leases in the background and releases them on completion. If a host dies, leases expire and another worker picks up the instance safely.

You can opt in by swapping the runtime and state store types:

var store = new ConcurrentSqliteStateStore("Data Source=functions.db", loggerFactory.CreateLogger<ConcurrentSqliteStateStore>());
var runtime = new ConcurrentDurableFunctionRuntime(store, loggerFactory.CreateLogger<ConcurrentDurableFunctionRuntime>(), loggerFactory: loggerFactory);

Stick to these types whenever you deploy more than one worker.

Mixing with Azure-compatible clients

Need to reuse existing tooling that expects Azure Durable Functions interfaces? The adapter in OrchestrationClientExample bridges the gap.

OrchestrationClientExample.cs
services.AddSingleton<IDurableOrchestrationClient>(provider =>
{
var coreClient = provider.GetRequiredService<IOrchestrationClient>();
return new DurableOrchestrationClientAdapter(coreClient);
});

This lets you run the lightweight Asynkron runtime locally while keeping compatibility with scripts or dashboards that speak the Azure interfaces.