Skip to main content

RESP010: share a long-lived client

RespireClient multiplexes connections and is safe to share across concurrent operations. Creating and disposing one for each request repeats connection setup and prevents reuse of its connection pools and caches.

async Task<string?> ReadAsync()
{
// RESP010
await using var client = await RespireClient.ConnectAsync("localhost");
return await client.GetStringAsync("key");
}

Create the client during application startup, or register it with dependency injection, then inject IRespireClient into request handlers. The application owns its disposal.

Task<string?> ReadAsync(IRespireClient client)
=> client.GetStringAsync("key").AsTask();

The bundled warning recognizes Create, ConnectAsync, and ConnectAnyAsync inside loops, and in using / await using declarations or statements inside methods. It leaves field initializers, factories returning a client, and the application entry point's outer lifetime alone. Loop checks stop at nested function boundaries. Top-level using declarations are outside the ownership check, but client creation in top-level loops is still checked. The ownership check also follows a local initialized by a creation call into a later using statement or declaration in the same block, provided the local is not written in the intervening statements or the disposal statement. It does not follow chains of aliases or disposal inside a nested function or another block. It does not infer DI service scopes, trace custom factories, or prove how frequently a method runs.

For a deliberate isolated client (for example, a connectivity probe), suppress the warning locally with #pragma warning disable RESP010 and restore it after the creation. An application-lifetime owner method other than Main may also need this suppression. No automatic code fix is offered: moving ownership into application startup requires choosing its configuration and disposal lifetime.

To change its severity in .editorconfig:

dotnet_diagnostic.RESP010.severity = suggestion