Spindle.Hosting
0.2.1
dotnet add package Spindle.Hosting --version 0.2.1
NuGet\Install-Package Spindle.Hosting -Version 0.2.1
<PackageReference Include="Spindle.Hosting" Version="0.2.1" />
<PackageVersion Include="Spindle.Hosting" Version="0.2.1" />
<PackageReference Include="Spindle.Hosting" />
paket add Spindle.Hosting --version 0.2.1
#r "nuget: Spindle.Hosting, 0.2.1"
#:package Spindle.Hosting@0.2.1
#addin nuget:?package=Spindle.Hosting&version=0.2.1
#tool nuget:?package=Spindle.Hosting&version=0.2.1
Spindle.Hosting
Spindle.Hosting wires the local Spindle runtime into the .NET generic host.
It gives you a background worker that advances persisted flow instances over time instead of running every flow to completion inside the caller.
This package is for the local MVP runtime. It uses persistence as the source of truth, polls runnable instances, executes local steps, fires due timers, and replays flows after progress is made.
Register Hosting
Register a store, register your flows, then add the worker.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Spindle;
using Spindle.Abstractions.Core;
using Spindle.Hosting;
using Spindle.Persistence;
using Spindle.Persistence.InMemory;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<ISpindleStore, InMemorySpindleStore>();
builder.Services.AddSpindleFlow<MyFlow, MyRequest, MyResult>(
new FlowName("my-flow"));
builder.Services.AddSpindleWorker(options =>
{
options.PollInterval = TimeSpan.FromMilliseconds(250);
options.MaxConcurrentFlowInstances = 4;
options.MaxStepsPerFlowPerTick = 1;
options.WorkerId = "worker-1";
});
await builder.Build().RunAsync();
AddSpindleWorker registers:
RuntimeSpindleRuntimeISpindleRuntimeISpindleRuntimePumpSpindleWorkerHostedService
Start Flows From Another Service
Any hosted service, controller, message consumer, or job can inject ISpindleRuntime and schedule a flow.
public sealed class TextConsumerService(
ISpindleRuntime runtime)
: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await runtime.StartAsync<TextRequest, TextResult>(
new FlowName("text-transform"),
new TextRequest("Hello Durable Workflows"),
new StartFlowOptions { IdempotencyKey = "text-1" },
stoppingToken);
}
}
The caller only creates the instance and performs the first expansion. The hosted Spindle worker later advances the instance by executing ready local steps, firing due timers, and replaying the flow.
Step Handlers
Register step handlers when flow code uses ctx.StepHandler(...).
builder.Services.AddSpindleStepHandler<MyHandler, MyHandlerRequest, MyHandlerResult>(
new StepHandlerId("my-handler"));
For this MVP, handlers are local process handlers. Queue-only workers and remote handlers are future work.
Options
SpindleHostOptions controls the local worker loop.
| Option | Default | Purpose |
|---|---|---|
PollInterval |
250ms |
Delay when no progress is made. |
MaxFlowInstancesPerTick |
100 |
Maximum runnable instances read per worker tick. |
MaxStepsPerFlowPerTick |
1 |
Maximum ready local steps executed for one flow advancement. |
MaxConcurrentFlowInstances |
CPU count | Maximum flow instances advanced concurrently. |
LeaseDuration |
30s |
Local step lease duration. |
WorkerId |
machine-based | Worker identity recorded on attempts and leases. |
Keeping MaxStepsPerFlowPerTick small is useful for long-running services because one flow cannot drain all ready work in one pump cycle.
Example
Run the hosting example:
dotnet run --project examples/Spindle.Example.Hosting/Spindle.Example.Hosting.csproj
The example has two hosted services:
SpindleWorkerHostedService, registered byAddSpindleWorker, which advances durable flow instances.TextConsumerService, which consumes text from a small in-memory inbox and starts one flow per text value.
The flow transforms each string into upper, lower, and camel-case outputs.
Current Limitations
- The in-memory store is for tests and local examples only.
- Only local
ImmediateandLocalWorkerstep execution is supported. - Queue dispatch, RabbitMQ, EF Core, remote flows, dashboards, source generators, and analyzers are not part of this package yet.
- The worker polls persistence. Future transport-backed wakeups can reduce polling and support distributed queue workers.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.8)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
- Spindle.Abstractions (>= 0.2.1)
- Spindle.Persistence.Abstractions (>= 0.2.1)
- Spindle.Runtime (>= 0.2.1)
-
net8.0
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
- Spindle.Abstractions (>= 0.2.1)
- Spindle.Persistence.Abstractions (>= 0.2.1)
- Spindle.Runtime (>= 0.2.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Spindle.Hosting:
| Package | Downloads |
|---|---|
|
Spindle.Testing
Testing utilities for Spindle.Net workflows. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.2.1 | 120 | 8/25/2026 |
| 0.2.0 | 111 | 8/25/2026 |
| 0.1.0 | 116 | 8/25/2026 |
| 0.1.0-preview.2 | 73 | 6/29/2026 |
| 0.1.0-preview.1 | 89 | 6/29/2026 |