From 3c2a2e2321f74f6dcf7da2b9cad9a066fb97beab Mon Sep 17 00:00:00 2001 From: jolov Date: Tue, 24 Mar 2026 12:49:01 -0700 Subject: [PATCH 1/3] fix: use wire info from original property for spread parameter matching Remove name-based fallback and use original property's wire info (via OriginalName) to populate the wire name dictionary for customized properties in spread conversion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Providers/ScmMethodProviderCollection.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs index dfbc4a42423..bb37b44b65c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs @@ -281,16 +281,28 @@ private List GetSpreadConversion(TypeProvider spreadSource) // Match convenience method parameters to constructor parameters by wire (serialized) name // to handle cases where C# names diverge due to @clientName renames, @encodedName, // or casing differences between the convenience parameters and model properties. - // Falls back to C# name matching for parameters without wire information. var convenienceMethodParamsByWireName = new Dictionary(StringComparer.OrdinalIgnoreCase); - var convenienceMethodParamsByName = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var p in ConvenienceMethodParameters) { if (p.WireInfo?.SerializedName != null) { convenienceMethodParamsByWireName.TryAdd(p.WireInfo.SerializedName, p); } - convenienceMethodParamsByName.TryAdd(p.Name, p); + } + + // For customized properties (where OriginalName identifies the original property), + // use the original property's wire info to ensure the dictionary has the correct mapping. + foreach (var property in spreadSource.CanonicalView.Properties) + { + if (property.OriginalName != null && property.WireInfo?.SerializedName is { } wireName) + { + var matchedParam = ConvenienceMethodParameters.FirstOrDefault( + p => string.Equals(p.WireInfo?.SerializedName, wireName, StringComparison.OrdinalIgnoreCase)); + if (matchedParam != null) + { + convenienceMethodParamsByWireName.TryAdd(wireName, matchedParam); + } + } } List expressions = new(spreadSource.Properties.Count); @@ -302,10 +314,7 @@ private List GetSpreadConversion(TypeProvider spreadSource) foreach (var param in ctor.Signature.Parameters) { var wireName = param.Property?.WireInfo?.SerializedName; - if (!(wireName != null && convenienceMethodParamsByWireName.TryGetValue(wireName, out var convenienceParam))) - { - convenienceMethodParamsByName.TryGetValue(param.Name, out convenienceParam); - } + convenienceMethodParamsByWireName.TryGetValue(wireName ?? string.Empty, out var convenienceParam); if (convenienceParam != null) { From 76e0370fccc057b825014ce89da180bb5621508d Mon Sep 17 00:00:00 2001 From: jolov Date: Tue, 24 Mar 2026 12:59:56 -0700 Subject: [PATCH 2/3] fix: null check wireName instead of defaulting to string.Empty Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/Providers/ScmMethodProviderCollection.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs index bb37b44b65c..bbb745c8fc8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs @@ -314,7 +314,11 @@ private List GetSpreadConversion(TypeProvider spreadSource) foreach (var param in ctor.Signature.Parameters) { var wireName = param.Property?.WireInfo?.SerializedName; - convenienceMethodParamsByWireName.TryGetValue(wireName ?? string.Empty, out var convenienceParam); + ParameterProvider? convenienceParam = null; + if (wireName != null) + { + convenienceMethodParamsByWireName.TryGetValue(wireName, out convenienceParam); + } if (convenienceParam != null) { From 66da9aaeeddcff9752a0a60be6e2db51602de9c1 Mon Sep 17 00:00:00 2001 From: jolov Date: Tue, 24 Mar 2026 13:31:05 -0700 Subject: [PATCH 3/3] fix: resolve wire name from model properties for custom constructors When a constructor parameter has no Property reference (custom code constructors), resolve its wire name by looking up the matching property in the model by parameter name. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Providers/ScmMethodProviderCollection.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs index bbb745c8fc8..73148e9ed9c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs @@ -305,6 +305,17 @@ private List GetSpreadConversion(TypeProvider spreadSource) } } + // Build a lookup from property name to wire name so we can resolve wire names + // for custom constructor parameters that don't have a Property reference. + var propertyWireNames = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var property in spreadSource.CanonicalView.Properties) + { + if (property.WireInfo?.SerializedName is { } propWireName) + { + propertyWireNames.TryAdd(property.Name, propWireName); + } + } + List expressions = new(spreadSource.Properties.Count); // we should make this find more deterministic var ctor = spreadSource.CanonicalView.Constructors.First(c => @@ -313,7 +324,14 @@ private List GetSpreadConversion(TypeProvider spreadSource) foreach (var param in ctor.Signature.Parameters) { + // Get wire name from the parameter's property if available, otherwise resolve + // from the model's properties by matching the parameter name to a property name. var wireName = param.Property?.WireInfo?.SerializedName; + if (wireName == null) + { + propertyWireNames.TryGetValue(param.Name, out wireName); + } + ParameterProvider? convenienceParam = null; if (wireName != null) {