Add AddNServiceBusInstallers for Generic Host Integration#7709
Conversation
d9aa002 to
789323d
Compare
…and improve the documentation
789323d to
e3b2b35
Compare
AddNServiceBusInstallers for Generic Host Integration
… installer execution and update related tests and documentation.
…onsolidate helper methods.
| /// Other registered <see cref="IHostedService"/> and <see cref="BackgroundService"/> implementations | ||
| /// may briefly start before shutdown takes effect. Services that cooperatively check the | ||
| /// <see cref="IHostApplicationLifetime.ApplicationStopping"/> cancellation token will gracefully abort. |
There was a problem hiding this comment.
So if you have a hosted service that you don't want even starting a breath, your options are:
- Inject IHostApplicationLifetime, check ApplicationStopping token before "doing anything important" but that may not signal immediately?
- Use the same check you're using to decide to include AddNServiceBusInstallers (cmd line param, config, envvar, whatever) to exit early from that work.
Is that right?
Are there any options for when you have a hosted service whose code you don't directly control?
There was a problem hiding this comment.
Yes, that’s basically right.
Those first two options are the practical ones for hosted services you own:
- Inject IHostApplicationLifetime and check ApplicationStopping before doing expensive or externally visible work. It won’t prevent StartAsync from being called, but it can help the service no-op quickly.
- Use the same condition you already use for
AddNServiceBusInstallers(config, env var, command line switch, etc.) and exit early from the hosted service.
For hosted services you don’t control or don't behave like "good host citizen", there usually isn’t a magic hook to stop the Generic Host from calling StartAsync once the service is registered. In practice the options are:
- Conditionally don’t register that hosted service in installer mode
- Use a separate installer-specific host/process that only wires up what is needed
- Build a dedicated composition path for installer runs
That’s really the core constraint here: once something is registered as an IHostedService, the host lifecycle is going to treat it like one.
As described in the PR description that captured our discussion we intentionally aligned with the normal Generic Host lifecycle instead of creating a separate/private execution path that bypasses it. That means there can be a brief start/stop window for hosted services during installer runs, but it keeps behavior consistent with standard hosting expectations I believe.
|
This PR introduces
AddNServiceBusInstallersonIServiceCollection, providing a modern, host-integrated alternative to the obsoleteInstaller.Setup()API.The new API enables infrastructure installation for all registered NServiceBus endpoints as part of the .NET Generic Host lifecycle. It is designed for CI/CD pipelines, deployment scenarios, and one-shot provisioning tasks while preserving composability and alignment with
Microsoft.Extensions.Hosting.This approach ensures that NServiceBus participates fully in the Generic Host rather than introducing a parallel execution path.
Motivation
EnableInstallers()only executes installers when an endpoint fully starts. Users who need one-shot installer execution, such as during deployment or automated provisioning, must rely on the obsoleteInstaller.Setup()API.AddNServiceBusInstallersprovides a modern, discoverable, and composable solution that:EndpointConfigurationas the canonical entry pointUsage
Normal Application Run
Behavior:
EnableInstallers().Install Infrastructure and Exit
Behavior:
Optional: Override Automatic Shutdown
This allows the host to remain running after installation without relying on registration order or implicit overrides.
Design Principles
EndpointConfiguration remains canonical
Endpoints are defined exactly as before.
Host-level installer mode
Installer execution is determined by the host, not individual endpoints.
All-or-nothing installation
In installer mode, infrastructure is installed for all registered endpoints.
Generic Host alignment
The implementation relies solely on public extension points such as hosted services and
IHostApplicationLifetime.No side-channel lifecycle
NServiceBus rides along with the Generic Host rather than bypassing it.
Multi-endpoint support
The design naturally supports applications hosting multiple endpoints.
Lifecycle Overview
The following diagrams illustrate how installer mode integrates with the .NET Generic Host lifecycle compared to a normal application run.
Installer Mode (
AddNServiceBusInstallers)sequenceDiagram autonumber participant App as Application participant Host as Generic Host participant Endpoint as NServiceBus Endpoints participant Installers as Installers participant Lifetime as Host Lifetime App->>Host: Build().RunAsync() Host->>Endpoint: StartingAsync() Endpoint->>Installers: Execute installers Installers-->>Endpoint: Completed Host->>Endpoint: StartAsync() (skipped) Host->>Lifetime: StopApplication() Lifetime-->>Host: Trigger shutdown Host-->>App: RunAsync() returnsNormal Application Run
sequenceDiagram autonumber participant App as Application participant Host as Generic Host participant Endpoint as NServiceBus Endpoints participant Runtime as Messaging Runtime App->>Host: Build().RunAsync() Host->>Endpoint: StartingAsync() Endpoint-->>Host: Prepared Host->>Endpoint: StartAsync() Endpoint->>Runtime: Start message processing Runtime-->>Endpoint: Running Note over Host: Application runs normallyEvolution from Previous Designs
PR #7683 —
AddNServiceBusEndpointInstallerIntroduced a dedicated installer-only registration. This approach implied a 1:1 relationship between endpoints and installers, which conflicted with the requirement to install infrastructure for all endpoints in a host.
PR #7691 —
InstallNServiceBusEndpointsonHostApplicationBuilderBuilt and discarded the host internally, bypassing the Generic Host lifecycle. This created a side-channel execution path and violated the principle that NServiceBus should integrate with, not circumvent, the host.
Final Design
AddNServiceBusInstallerstreats installers as a host-level additive concern, ensuring full alignment with the Generic Host and avoiding parallel execution paths until we have a way to properly generated deployment manifests and separate deployments into dedicated CLI tools similar to EF migration.Trade-offs
The brief start-and-stop behavior is the honest cost of participating in the Generic Host lifecycle.
Benefits
Installer.Setup()EndpointConfigurationas the canonical entry point