Skip to content

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

Merged
JoshLove-msft merged 39 commits into
mainfrom
copilot/auto-generate-clientsettings-class
Mar 13, 2026
Merged

[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors#9920
JoshLove-msft merged 39 commits into
mainfrom
copilot/auto-generate-clientsettings-class

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 4, 2026

  • Create ClientSettingsProvider.cs - new TypeProvider generating {Client}Settings class extending ClientSettings
  • Modify ClientOptionsProvider.cs - add internal {Client}Options(IConfigurationSection section) constructor with type-aware property binding
  • Modify ClientProvider.cs - add public {Client}({Client}Settings settings) constructor and internal (AuthenticationPolicy?, endpoint, options) implementation constructor
  • Modify ScmOutputLibrary.cs - register ClientSettingsProvider in the output
  • All previous review feedback addressed
  • Restore global.json to base branch value (10.0.103)
  • Apply all code review nits from @jorgerangel-msft
  • Revert StubLibraryVisitor to keep internal ctors (per @JoshLove-msft)
  • Sort LatestVersionsFields dictionary before consuming in ClientOptionsProvider
  • Add XML docs with see cref link to ClientSettings on generated Settings class
  • Update ClientSettings class doc to follow pattern: "Represents the settings used to configure a {Client} that can be loaded from an IConfigurationSection."
  • Fix OAuth2 Spector tests: _flows is now static readonly, use BindingFlags.Static and GetValue(null) instead of BindingFlags.Instance
  • Extract CredentialParamName and SettingsParamName constants in ClientProvider.cs for consistency
  • Add all type binding patterns in ClientSettingsProvider.BindCore(): string, bool, int, TimeSpan (TryParse), Uri (TryCreate), List<string> (GetSection/GetChildren/Where/Select/ToList), extensible enums (new TypeName(val)), fixed enums (Enum.TryParse), and complex objects (GetSection + Exists + new Type(section))
  • Add unit tests for all supported type bindings (string, bool, int, TimeSpan, Uri, List<string>, extensible enum, fixed enum, complex object) and multi-type property scenarios
  • Fix duplicated assertions in sub-client constructors (addExplicitValidation only for internal ctors)
  • Fix CS1729 build error: fixed C# enums (like ClientType) now use Enum.TryParse instead of new TypeName(val) constructor
  • Add generic complex object binding handler for non-framework, non-enum types in OtherRequiredParams
  • Refactor binding helper methods into shared internal static methods on ClientSettingsProvider for reuse by both ClientSettingsProvider and ClientOptionsProvider
  • Add type-aware binding for ClientOptions IConfigurationSection constructor — properties now use correct binding patterns (bool.TryParse, int.TryParse, TimeSpan.TryParse, Uri.TryCreate, List<string>, extensible/fixed enums, complex objects) instead of treating all properties as strings
  • Add unit tests for ClientOptions IConfigurationSection constructor with non-string property types (bool, int, string, extensible enum, fixed enum, complex object)
  • Run Generate.ps1 and check in generated changes
  • Add IsNot snippet to ValueExpression and ParameterProvider for is not pattern expression support, used in ClientSettingsProvider string list binding
  • Ensure all Spector tests pass
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
@JoshLove-msft
Copy link
Copy Markdown
Contributor

@copilot run Generate.ps1 and investigate any failures.

…ClientModel 1.9.0 instead of string-based type reference

Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Copilot AI and others added 2 commits March 5, 2026 04:04
…guard, fix API key credential in settings constructor, fix missing default ctor

Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
… fix API key credential extraction, add default ctor, add package dep

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

Copilot AI commented Mar 5, 2026

@copilot run Generate.ps1 and investigate any failures.

Run complete in 04121e3. Generate.ps1 succeeded and the full solution builds with 0 errors. Fixed issues found:

  • Missing default constructor in ClientOptionsProvider when no API versions (suppressed the auto-generated one)
  • Invalid CredentialProvider as ApiKeyCredential cast (replaced with settings?.Credential?.Key != null ? new ApiKeyCredential(settings?.Credential?.Key) : null)
  • Missing non-auth required parameters in settings constructor (e.g. ClientType client, string serviceDeploymentVersion) — ClientSettingsProvider now collects and exposes OtherRequiredParams, generating properties and correct constructor args for them

…on constructor before guard

Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
@JoshLove-msft JoshLove-msft marked this pull request as ready for review March 10, 2026 22:05
@JoshLove-msft
Copy link
Copy Markdown
Contributor

@copilot

ompilation completed successfully.

Building http/client/structure/client-operation-group

dotnet build /mnt/vss/_work/1/s/packages/http-client-csharp/generator/TestProjects/Spector/http/client/structure/client-operation-group/src/Client.Structure.Service.csproj
Determining projects to restore...
All projects are up-to-date for restore.
/mnt/vss/_work/1/s/packages/http-client-csharp/generator/TestProjects/Spector/http/client/structure/client-operation-group/src/Generated/SubNamespaceSecondClientSettings.cs(36,30): error CS1729: 'ClientType' does not contain a constructor that takes 1 arguments [/mnt/vss/_work/1/s/packages/http-client-csharp/generator/TestProjects/Spector/http/client/structure/client-operation-group/src/Client.Structure.Service.csproj::TargetFramework=net8.0]
/mnt/vss/_work/1/s/packages/http-client-csharp/generator/TestProjects/Spector/http/client/structure/client-operation-group/src/Generated/FirstClientSettings.cs(36,30): error CS1729: 'ClientType' does not contain a constructor that takes 1 arguments [/mnt/vss/_work/1/s/packages/http-client-csharp/generator/TestProjects/Spector/http/client/structure/client-operation-group/src/Client.Structure.Service.csproj::TargetFramework=net8.0]

Build FAILED.

/mnt/vss/_work/1/s/packages/http-client-csharp/generator/TestProjects/Spector/http/client/structure/client-operation-group/src/Generated/SubNamespaceSecondClientSettings.cs(36,30): error CS1729: 'ClientType' does not contain a constructor that takes 1 arguments [/mnt/vss/_work/1/s/packages/http-client-csharp/generator/TestProjects/Spector/http/client/structure/client-operation-group/src/Client.Structure.Service.csproj::TargetFramework=net8.0]
/mnt/vss/_work/1/s/packages/http-client-csharp/generator/TestProjects/Spector/http/client/structure/client-operation-group/src/Generated/FirstClientSettings.cs(36,30): error CS1729: 'ClientType' does not contain a constructor that takes 1 arguments [/mnt/vss/_work/1/s/packages/http-client-csharp/generator/TestProjects/Spector/http/client/structure/client-operation-group/src/Client.Structure.Service.csproj::TargetFramework=net8.0]
0 Warning(s)
2 Error(s)

…Core

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

Copilot AI commented Mar 12, 2026

@copilot

ompilation completed successfully.
...

Fixed in 51fbd66. The build failure was caused by generating new ClientType(val) for fixed C# enums, which don't have string constructors. Fixed ClientSettingsProvider to distinguish between extensible enums (IsStruct == truenew TypeName(val)) and fixed enums (IsStruct == falseEnum.TryParse(section[name], out TypeName val)). Added AppendFixedEnumBinding method and a dedicated TestBindCoreMethod_WithFixedEnumParam unit test.

@JoshLove-msft
Copy link
Copy Markdown
Contributor

@copilot what about complex objects? Did we add handling and test cases for those?

Copilot AI and others added 2 commits March 12, 2026 20:32
Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 12, 2026

@copilot what about complex objects? Did we add handling and test cases for those?

Added in 60a3de0. Complex objects now use the section.GetSection(name) + .Exists() + new Type(section) pattern via AppendComplexObjectBinding. Previously, non-framework, non-enum types in OtherRequiredParams were silently skipped — now they get the complex object binding. The Options binding (already present) also uses this same pattern. Added TestBindCoreMethod_WithComplexObjectParam test using a mock model type parameter.

@JoshLove-msft
Copy link
Copy Markdown
Contributor

@copilot what about complex objects? Did we add handling and test cases for those?

Added in 60a3de0. Complex objects now use the section.GetSection(name) + .Exists() + new Type(section) pattern via AppendComplexObjectBinding. Previously, non-framework, non-enum types in OtherRequiredParams were silently skipped — now they get the complex object binding. The Options binding (already present) also uses this same pattern. Added TestBindCoreMethod_WithComplexObjectParam test using a mock model type parameter.

@copilot do we handle complex object properties on the ClientOptions? This last commit added handling for ctor parameters, but what about ClientOption properties?

…uctor

Refactored binding helper methods in ClientSettingsProvider to be internal
static and reusable. ClientOptionsProvider now uses AppendBindingForProperty
for type-aware binding of all property types (string, bool, int, TimeSpan,
Uri, List<string>, extensible/fixed enums, complex objects) instead of
treating all properties as strings.

Added tests for bool, int, string, extensible enum, fixed enum, and
complex object property bindings in ClientOptionsProviderTests.

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

Copilot AI commented Mar 12, 2026

@copilot what about complex objects? Did we add handling and test cases for those?

Added in [60a3de0](60a3de0cbad5b18b3147bd24d0...

Yes, handled in ca2320b. Refactored the binding helper methods in ClientSettingsProvider to be internal static and reusable, then updated ClientOptionsProvider.BuildConfigurationSectionConstructor() to use ClientSettingsProvider.AppendBindingForProperty() for type-aware binding of all property types — string, bool, int, TimeSpan, Uri, List<string>, extensible enums, fixed enums, and complex objects — instead of treating all properties as strings.

Added 6 new tests for ClientOptions properties: TestConfigurationSectionConstructorBody_WithBoolProperty, WithIntProperty, WithStringProperty, WithEnumProperty, WithFixedEnumProperty, and WithComplexObjectProperty.

… numeric types

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
…nual VariableExpression

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copilot AI and others added 2 commits March 12, 2026 23:03
Co-authored-by: JoshLove-msft <102122018+JoshLove-msft@users.noreply.github.com>
… in ClientSettingsProvider

Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
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

5 participants