Skip to content

Support multi-service C# clients without @client decorator#10438

Merged
jorgerangel-msft merged 8 commits intomainfrom
copilot/add-support-for-multiservice-clients
Apr 24, 2026
Merged

Support multi-service C# clients without @client decorator#10438
jorgerangel-msft merged 8 commits intomainfrom
copilot/add-support-for-multiservice-clients

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 21, 2026

When a TypeSpec library declares multiple @service-decorated namespaces without an explicit @client (the service/multiple-services spector scenario), TCGC emits one root client per service. The C# emitter's library-level logic didn't recognize this shape and resolved the package namespace to the first service's namespace instead of the shared parent, and the generator incorrectly shared a single ClientOptions instance across the per-service root clients.

Emitter changes (emitter/src/lib/utils.ts)

  • getClientNamespaceString — when there are multiple @service namespaces (i.e. serviceNamespaces.length > 1) and the library does not contain a combined multi-service client, resolves the library namespace to the TypeSpec parent Namespace shared by those services (via firstNamespace.namespace from listAllServiceNamespaces) and passes it through the existing namespace-based helper path. The containsMultiServiceClient check is evaluated first so that a combined @client({ service: [...] }) under multiple nested @service namespaces (e.g. the service/multi-service spector spec) still resolves to the combined client's own namespace (e.g. Service.MultiService.Combined) rather than being truncated to the shared parent.
  • containsMultiServiceClient — simplified to rootClients.some(isMultiServiceClient) so it only returns true when an individual client contains more than one service. The multi-root-multi-service case (multiple @service namespaces, each a separate single-service root) is handled directly in getClientNamespaceString via the serviceNamespaces.length > 1 check.

For the multiple-services spec, the library namespace now resolves to Service.MultipleServices instead of Service.MultipleServices.ServiceA, with ServiceAClient and ServiceBClient as sibling root clients each exposing their own Operations / SubNamespace sub-clients and per-service API version enums.

Generator changes (ClientOptionsProvider.cs)

  • UseSingletonInstance — also returns false when the input library contains more than one ApiVersionEnum (i.e. spans multiple services). Previously, when multiple per-service single-service root clients existed, both would share the singleton ClientOptionsProvider, producing a single options class with only the first service's api version. Each root client now gets its own ClientOptions class (e.g. ServiceAClientOptions, ServiceBClientOptions).
  • Constructor — now derives the service-specific api version enum(s) from the client's own api-version-typed parameters (p.IsApiVersion && p.Type is InputEnumType), falling back to the library-wide enum list only when the client has no typed api-version parameter. Previously, the constructor always scanned all library enums and (for single-service clients) picked the first one, causing ServiceBClientOptions to nest ServiceA's api version enum.

Tests

  • emitter/test/Unit/client-converter.test.ts — new describe("multiple services without @client decorator", ...) block mirroring the spector scenario, asserting root count/names/namespaces, sub-client shape, per-service apiVersions, and that containsMultiServiceClient returns false (since no individual root client is itself a combined multi-service client). Also adds a regression test for the combined @client with nested @service namespaces scenario (multi-service spector spec) asserting the root namespace is the combined client's namespace (e.g. Service.MultiService.Combined).
  • generator/.../ClientOptionsProviderTests.cs — new MultipleRootClients_WithoutClientDecorator_EachGeneratesItsOwnClientOptions test verifying that two per-service root clients each produce their own distinct ClientOptions type with the expected names and that each options class nests its own service-specific api version enum (verified via the enum's InputNamespace).

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/10b8061a-9ce0-4c76-ab95-d30a73d2a6df

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp label Apr 21, 2026
Copilot AI changed the title [WIP] Add support for multiService clients with no client decorator Support multi-service C# clients without @client decorator Apr 21, 2026
Copilot AI requested a review from jorgerangel-msft April 21, 2026 17:54
Comment thread packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts Outdated
Comment thread packages/http-client-csharp/emitter/src/lib/utils.ts Outdated
…ant test comment

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/4bd1ac3b-75cd-410c-bd84-0700b628702e

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 21, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-client-csharp@10438

commit: 71bf03f

Comment thread packages/http-client-csharp/emitter/src/lib/utils.ts Outdated
Comment thread packages/http-client-csharp/emitter/src/lib/utils.ts Outdated
…for multi-service libraries

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/b533b9d9-348c-43f4-bd92-bb9ad5af287f

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Comment thread packages/http-client-csharp/emitter/src/lib/utils.ts Outdated
Comment thread packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts Outdated
…ient to detect multi-root multi-service libraries

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/ef5611a0-3a47-4880-9b8d-695a47748e5a

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/eee2ac08-17bf-4c2a-881f-415a53f68b4e

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
@jorgerangel-msft
Copy link
Copy Markdown
Contributor

…ice options api version

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/2b0b711b-991b-4b7f-915c-f84ef3863da1

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 24, 2026

@copilot there is a regression when regenerating our azure test libraries for this spec https://github.com/Azure/typespec-azure/blob/main/packages/azure-http-specs/specs/service/multi-...

Fixed both regressions in 3dd66c4.

  1. Namespace truncation for multi-service: Reordered checks in getClientNamespaceString so containsMultiServiceClient is evaluated before the serviceNamespaces.length > 1 branch. A combined @client({ service: [...] }) spanning multiple @service namespaces now correctly resolves to the combined client's namespace (e.g. Service.MultiService.Combined) instead of the shared parent. Added an emitter regression test for this.

  2. ServiceBClientOptions using ServiceA's api version: The ClientOptionsProvider constructor was scanning all api-version enums in the library and picking the first one for single-service clients. Now it first looks at the client's own api-version parameter(s) for the enum type (falling back to the library enum list only when the client has no typed api-version parameter, to preserve existing test setups). Strengthened the existing unit test to assert each root client's options nests its own service-specific api version enum (verified via the enum's InputNamespace).

All 1312 ClientModel generator tests and 201 emitter tests pass.

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/9dfd093d-7997-4a3a-9d47-2231389307bf

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
@jorgerangel-msft jorgerangel-msft added this pull request to the merge queue Apr 24, 2026
Merged via the queue into main with commit 8b69f39 Apr 24, 2026
29 checks passed
@jorgerangel-msft jorgerangel-msft deleted the copilot/add-support-for-multiservice-clients branch April 24, 2026 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Support For MultiService Clients with No Client Decorator

3 participants