[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors#9920
[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors#9920
Conversation
…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>
| private readonly InputEndpointParameter? _inputEndpointParam; | ||
|
|
||
| // ClientSettings base type - not yet in current NuGet package | ||
| internal static readonly CSharpType ClientSettingsType = CSharpType.FromExternalType("ClientSettings", "System.ClientModel.Primitives"); |
There was a problem hiding this comment.
@copilot we just need to upgrade to the version of System.ClientModel that has ClientSettings.
There was a problem hiding this comment.
Done in 78afaca. System.ClientModel 1.9.0 (already the pinned version) does have ClientSettings in System.ClientModel.Primitives. Replaced the FromExternalType string-based workaround with typeof(ClientSettings) (with scoped #pragma warning disable SCME0002 since it's experimental) and typeof(IConfigurationSection). Also removed the FromExternalType method from CSharpType.cs since it's no longer needed.
...p/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientSettingsProvider.cs
Outdated
Show resolved
Hide resolved
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/CSharpType.cs
Outdated
Show resolved
Hide resolved
|
@copilot run Generate.ps1 and investigate any failures. |
...rp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs
Outdated
Show resolved
Hide resolved
…ClientModel 1.9.0 instead of string-based type reference Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
…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>
Run complete in 04121e3. Generate.ps1 succeeded and the full solution builds with 0 errors. Fixed issues found:
|
...rp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/SampleTypeSpecClientOptions.cs
Show resolved
Hide resolved
…on constructor before guard Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Automatically generates
IConfiguration-based client construction support for every generated client, enabling standard .NET configuration patterns (appsettings.json, environment variables) and dependency injection registration.What's Generated
A.
{Client}SettingsclassClientSettings(fromSystem.ClientModel.Primitives)BindCore(IConfigurationSection section)override[Experimental("SCME0002")]B.
internal {Client}Options(IConfigurationSection section)constructorbase(section)for common property forwardingVersionto the latest version string before the guard (ensuring it's always initialized)if (section is null || !section.Exists()) { return; }before binding[Experimental("SCME0002")]C.
public {Client}({Client}Settings settings)constructorthis(...)initializersettings?.Credential?.Key != null ? new ApiKeyCredential(settings?.Credential?.Key) : nullsettings?.CredentialProvider as AuthenticationTokenProvider[Experimental("SCME0002")]Changes Made
ClientSettingsProvider.cs— newTypeProvidergenerating{Client}Settings; collectsOtherRequiredParams(non-auth, non-endpoint required constructor parameters) and generates properties andBindCorebindings for themClientOptionsProvider.cs— addsIConfigurationSectionconstructor with version initialization before guard; adds default parameterless constructor when no API versions are presentClientProvider.cs— adds settings constructor; passes non-auth required parameters in correct order using?? defaultfor value typesScmOutputLibrary.cs— registersClientSettingsProviderin the outputPackages.Data.props/.csproj— adds explicitMicrosoft.Extensions.Configuration.Abstractions(v10.0.2) dependencyCSharpType.cs— removedFromExternalType; uses directtypeof(ClientSettings)with scoped#pragma warning disable SCME0002Testing
Generate.ps1completes successfully; full solution builds with 0 errorsClientSettingsProvider,ClientOptionsProviderIConfiguration constructor, andClientProvidersettings constructorOriginal 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}SettingsclassClientSettings(fromSystem.ClientModel.Primitives)BindCore(IConfigurationSection section)override[Experimental("SCME0002")]B.
internal {Client}Options(IConfigurationSection section)constructorbase(section)— the base options class handles common properties (see Forwarding Binding)if (section is null || !section.Exists()) { return; }before binding any properties[Experimental("SCME0002")]C.
public {Client}({Client}Settings settings)constructor on the client(AuthenticationPolicy, Options)or(Uri, AuthenticationPolicy, Options)depending on the clientAuthenticationPolicy.Create(settings)to pass to the primary constructorsettings?.Endpoint,settings?.Options)[Experimental("SCME0002")]How to Determine Settings Properties
The generator should inspect the client's primary public constructor (the one with the body implementation) parameters:
Uri?properties on Settings (e.g.,VaultUri,Endpoint){Client}Options?property on SettingsClientSettings.Credential/CredentialProvider, NOT duplicatedHow to Implement BindCore
`csharp
protected override void BindCore(IConfigurationSection section)
{
if (section is null || !section.Exists())
{
return;
}
}
`
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:stringsection[name] is string valTenantId = valboolbool.TryParse(section[name], out bool val)DisableChallengeResourceVerification = valUriUri.TryCreate(section[name], UriKind.Absolute, out Uri val)RedirectUri = valTimeSpanTimeSpan.TryParse(section[name], out TimeSpan val)NetworkTimeout = valintint.TryParse(section[name], out int val)MaxRetries = valstring[]/List<string>section.GetSection(name).GetChildren().Where(c => c.Value is not null).Select(c => c.Value!).ToList()AdditionallyAllowedTenantssection.GetSection(name)+.Exists()+new Type(section)BrowserCustomization = new BrowserCustomizationOptions(browserSection)new TypeName(section[name])after null checkAudience = 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.