Skip to content

[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors#9920

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/auto-generate-clientsettings-class
Draft

[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors#9920
Copilot wants to merge 3 commits intomainfrom
copilot/auto-generate-clientsettings-class

Conversation

Copy link
Contributor

Copilot AI commented Mar 4, 2026

The .NET generator did not produce IConfiguration-based client construction support, requiring manual implementation per client. This adds automatic generation of the three artifacts needed for every root client to support standard .NET configuration patterns (appsettings.json, env vars, DI containers).

New: ClientSettingsProvider

Generates {Client}Settings : ClientSettings (System.ClientModel.Primitives) with:

  • Uri? {EndpointName} and {Client}Options? Options properties (credential handled by base CredentialProvider)
  • BindCore(IConfigurationSection section) override binding endpoint and options from config
  • [Experimental("SCME0002")] on the class

Modified: ClientOptionsProvider

Adds an internal IConfigurationSection constructor forwarding to base(section), with an early-exit null/exists guard, binding non-version service-specific properties.

Modified: ClientProvider

Adds a public {Client}({Client}Settings settings) constructor delegating to the primary constructor — only for root clients with a configurable endpoint:

[Experimental("SCME0002")]
public SecretClient(SecretClientSettings settings)
    : this(settings?.VaultUri, settings?.CredentialProvider as TokenCredential, settings?.Options)
{ }

Modified: ScmOutputLibrary / ClientProvider

  • ClientProvider exposes a ClientSettings property (ClientSettingsProvider?)
  • ScmOutputLibrary.BuildClient registers the ClientSettingsProvider in the output type set alongside ClientOptions

Notes

  • Sub-clients do not get settings classes (only root clients with ClientOptions != null)
  • ClientSettings and IConfigurationSection are referenced by name via a new CSharpType factory for external types not yet in the pinned System.ClientModel 1.9.0 — generated code will compile once the upstream package ships these types

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/Azure/azure-sdk-for-net/contents/sdk/core/System.ClientModel/src
    • Triggering command: /usr/bin/curl curl -s REDACTED (http block)
  • https://api.github.com/repos/Azure/azure-sdk-for-net/git/trees/main
    • Triggering command: /usr/bin/curl curl -s REDACTED (http block)
  • www.nuget.org
    • Triggering command: /usr/bin/curl curl -L -s REDACTED (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors for clients</issue_title>
<issue_description>## Overview

Parent issue: Azure/azure-sdk-for-net#55491

The .NET generator should automatically produce IConfiguration-based client construction support for every generated client. This enables developers to configure clients using standard .NET configuration patterns (appsettings.json, environment variables) and register them with dependency injection containers.

Two services have been manually implemented as reference:

The generator should produce these automatically so every client gets configuration support without manual customization.

What the Generator Needs to Produce

Three artifacts per client:

A. {Client}Settings class

  • Extends ClientSettings (from System.ClientModel.Primitives)
  • Properties derived from the client's primary constructor parameters (excluding credential)
  • Implements BindCore(IConfigurationSection section) override
  • Marked [Experimental("SCME0002")]

B. internal {Client}Options(IConfigurationSection section) constructor

  • Calls base(section) — the base options class handles common properties (see Forwarding Binding)
  • Guards with if (section is null || !section.Exists()) { return; } before binding any properties
  • Binds each option-specific property from the section
  • Marked [Experimental("SCME0002")]

C. public {Client}({Client}Settings settings) constructor on the client

  • Delegates to the client's primary constructor (the one with the body implementation) — e.g., (AuthenticationPolicy, Options) or (Uri, AuthenticationPolicy, Options) depending on the client
  • Creates the authentication policy via AuthenticationPolicy.Create(settings) to pass to the primary constructor
  • Extracts other properties from settings and maps them to the primary constructor parameters (e.g., settings?.Endpoint, settings?.Options)
  • Not all clients have a URI parameter — the settings class properties mirror whatever the primary constructor requires
  • Marked [Experimental("SCME0002")]

How to Determine Settings Properties

The generator should inspect the client's primary public constructor (the one with the body implementation) parameters:

  • Endpoint/URI parameters (if present) → become Uri? properties on Settings (e.g., VaultUri, Endpoint)
  • Options parameter → becomes {Client}Options? property on Settings
  • Credential/AuthenticationPolicy parameter → handled by base ClientSettings.Credential / CredentialProvider, NOT duplicated
  • Any other non-credential constructor parameters may become settings properties

How to Implement BindCore

`csharp
protected override void BindCore(IConfigurationSection section)
{
if (section is null || !section.Exists())
{
return;
}

// String → use 'is string' pattern match
if (section["Endpoint"] is string endpoint)
{
    Endpoint = new Uri(endpoint);
}

// Options → GetSection + Exists check + internal ctor
IConfigurationSection optionsSection = section.GetSection("Options");
if (optionsSection.Exists())
{
    Options = new {Client}Options(optionsSection);
}

}
`

Key rule: If a configuration segment doesn't exist, bail early — don't set the property. No validation in BindCore. Let the client constructor that receives the settings do all required parameter validation (e.g., Argument.AssertNotNull).

Type Binding Patterns

The generator should use these patterns to bind properties from IConfigurationSection:

Type Pattern Example
string section[name] is string val TenantId = val
bool bool.TryParse(section[name], out bool val) DisableChallengeResourceVerification = val
Uri Uri.TryCreate(section[name], UriKind.Absolute, out Uri val) RedirectUri = val
TimeSpan TimeSpan.TryParse(section[name], out TimeSpan val) NetworkTimeout = val
int int.TryParse(section[name], out int val) MaxRetries = val
string[] / List<string> section.GetSection(name).GetChildren().Where(c => c.Value is not null).Select(c => c.Value!).ToList() AdditionallyAllowedTenants
Complex object section.GetSection(name) + .Exists() + new Type(section) BrowserCustomization = new BrowserCustomizationOptions(browserSection)
Custom struct/enum (extensible) new TypeName(section[name]) after null check Audience = new AppConfigurationAudience(audience)

For a comprehensive example of bin...


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits March 4, 2026 20:51
…onfiguration-enabled constructors

- Add new ClientSettingsProvider class that generates {Client}Settings classes
  - Extends ClientSettings from System.ClientModel.Primitives (future type)
  - Properties for endpoint (Uri?) and options ({Client}Options?)
  - BindCore override method binding from IConfigurationSection
  - Marked [Experimental("SCME0002")]
- Add IConfigurationSection constructor to ClientOptionsProvider
  - Internal constructor calling base(section)
  - Guards with null/Exists check
  - Binds non-version string properties from configuration
  - Marked [Experimental("SCME0002")]
- Add settings constructor to ClientProvider for root clients with configurable endpoints
  - Calls primary constructor via this() initializer
  - Passes endpoint, credential (if auth), and options from settings
  - Marked [Experimental("SCME0002")]
- Add ClientSettingsProvider output in ScmOutputLibrary.BuildClient
- Add CSharpType.FromExternalType() public factory method for types not in current NuGet
- Update tests to account for new constructors and generated code

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Settings feature

- Use HashSet for O(1) version property name lookup in BuildConfigurationSectionConstructor
- Rename local variable from 'propValue' suffix to 'FromConfig' for clarity
- Extract IsSettingsConstructor helper in test for maintainability

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI changed the title [WIP] Add auto-generation of ClientSettings class and IConfiguration constructors [.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors Mar 4, 2026
@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 Mar 4, 2026
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.

[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors for clients

2 participants