A scope is a child of the root provider with its own cache of scoped services.
Singletons come from the root and are shared; scoped services are created once per scope; transients are created per
resolution and owned by the scope that resolved them. It is .NET’s IServiceScope.
The randomUUID() method of the Crypto interface is used to generate a v4 UUID using a cryptographically secure random number generator.
Available only in secure contexts.
await scope.dispose() disposes every scoped and transient instance the scope created, in reverse creation order,
by calling onDestroy(), [Symbol.asyncDispose]() or [Symbol.dispose](). Singletons are untouched: they belong to
the root. Details in Lifecycle and disposal.
ServiceProvider implements Symbol.asyncDispose, so await using calls dispose() for you when the block exits,
normally or by an exception. On older setups write it out:
const
constscope:ServiceProvider
scope=
constprovider:ServiceProvider
provider.
ServiceProvider.createScope(): ServiceProvider
Creates a scope: scoped services resolve once per scope. Dispose it when done (or use await using).
createScope();
try {
await
functionwork(scope:ServiceProvider):Promise<void>
work(
constscope:ServiceProvider
scope);
} finally {
await
constscope:ServiceProvider
scope.
ServiceProvider.dispose(): Promise<void>
Disposes every instance this provider (or scope) created, in reverse creation order:
onDestroy(), else [Symbol.asyncDispose](), else [Symbol.dispose](). Disposing the root
disposes singletons; disposing a scope disposes its scoped and transient instances.
Every instance is disposed even if some throw; the errors are rethrown afterwards
(an AggregateError if more than one). Later calls wait for the first one and do not rethrow.
dispose();
}
Calling dispose() again, or concurrently, waits for the first disposal to finish and never rethrows its errors.
After it, every get* call and createScope() on that scope throws DisposedError, and isService returns false.
If a scope is disposed while one of its services is still being built (an async factory or onInit still running),
that service is disposed as soon as it is finished, and the pending get* call rejects with DisposedError.
Nothing created for a disposed scope is leaked.
createScope() can be called on the root or on a scope. Either way the new scope hangs off the root: scopes don’t
nest, and a scope created from another scope does not share its scoped instances.
Disposing the root disposes its singletons (and the transients resolved from the root). After that, every scope of
that root throws DisposedError too, and isService returns false on them.
Disposing the root does not dispose scoped services of scopes still open. Dispose scopes when their work ends,
then the root at shutdown.
// Graceful shutdown: stop taking work, let open scopes finish, then dispose singletons.
constprocess: {
once(event:string, fn: () =>void):void;
}
process.
functiononce(event:string, fn: () =>void):void
once('SIGTERM', async () => {
await
constserver: {
close():Promise<void>;
}
server.
functionclose():Promise<void>
close();
await
constprovider:ServiceProvider
provider.
ServiceProvider.dispose(): Promise<void>
Disposes every instance this provider (or scope) created, in reverse creation order:
onDestroy(), else [Symbol.asyncDispose](), else [Symbol.dispose](). Disposing the root
disposes singletons; disposing a scope disposes its scoped and transient instances.
Every instance is disposed even if some throw; the errors are rethrown afterwards
(an AggregateError if more than one). Later calls wait for the first one and do not rethrow.
buildServiceProvider() copies the registrations. Adding, removing or replacing services in the
ServiceCollection afterwards has no effect on that provider or its scopes. To change wiring, change the collection
and build a new provider, which is what test setups do.