Conversation
…iple underscores Use the first underscore as the separator in MultipleClientsFromOperationIdOperationNameGenerator: - GetClientName: everything before the first '_' = client name - GetOperationName: everything after the first '_' = operation name This guarantees uniqueness since OpenAPI requires globally unique operation IDs. Previously, 'Orders_items_get' and 'Products_items_get' both produced client='items', operation='get', causing duplicate generated code. Co-authored-by: lahma <171892+lahma@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a long-standing bug in MultipleClientsFromOperationIdOperationNameGenerator where operation IDs with multiple underscores (e.g., Orders_items_get and Products_items_get) would map to duplicate (client, operation) pairs, causing duplicate generated classes and methods. The fix simplifies the splitting strategy to always use the first underscore as the delimiter — everything before it becomes the client name, everything after becomes the operation name. Since OpenAPI mandates globally unique operation IDs, this guarantees uniqueness of (client, operation) pairs.
Changes:
GetClientName: Returns everything before the first underscore (instead of the second-to-last segment)GetOperationName: Returns everything after the first underscore (instead of the last segment)- Test updates and additions to cover the new behavior and regression-test uniqueness guarantees
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/NSwag.CodeGeneration/OperationNameGenerators/MultipleClientsFromOperationIdOperationNameGenerator.cs |
Simplified GetClientName and GetOperationName to split on the first underscore instead of the last |
src/NSwag.CodeGeneration.Tests/CodeGenerationTests.cs |
Updated existing test expectations to match new behavior, added operation-name tests for MultipleClientsFromOperationId, and added regression tests asserting uniqueness of (client, operation) pairs |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Updated [NSwag.Core](https://github.com/RicoSuter/NSwag) from 14.6.3 to 14.7.0. <details> <summary>Release notes</summary> _Sourced from [NSwag.Core's releases](https://github.com/RicoSuter/NSwag/releases)._ ## 14.7.0 ## What's Changed * Upgrade to NUKE 10 by @lahma in RicoSuter/NSwag#5310 * Upgrade to NUKE 10.1.0 by @lahma in RicoSuter/NSwag#5314 * Fix specifying runtime for `NSwag.Npm` resulting in a `NConsole.UnusedArgumentException` by @ptasev in RicoSuter/NSwag#5320 * fix: nullpointer by @MeikelLP in RicoSuter/NSwag#5337 * fix axios template: [object Object] is not valid JSON. by @adnanalbeda in RicoSuter/NSwag#5283 * Fix legacy document transformation by @copyleftproducts in RicoSuter/NSwag#5315 * Fix compile error in generated C# client when media type contains quotes by @bkoelman in RicoSuter/NSwag#5345 * Fix duplicate client/operation names when operation IDs contain multiple underscores by @Copilot in RicoSuter/NSwag#5348 * Update to NJsonSchema v11.6.0 and Namotion.Reflection v3.5.0 (v14.7.0) by @RicoSuter in RicoSuter/NSwag#5357 ## NJsonSchema v11.6.0 (potentially breaking changes) This release updates to [NJsonSchema v11.6.0](https://github.com/RicoSuter/NJsonSchema/releases/tag/v11.6.0) and [Namotion.Reflection v3.5.0](https://github.com/RicoSuter/Namotion.Reflection/commits/master/), which include the following changes that may affect generated code: - **C# 11 `required` keyword now correctly recognized**: Properties using the C# 11 `required` keyword (via `RequiredMemberAttribute` / `JsonRequiredAttribute`) are now properly treated as required in the schema and generated code. Previously these were incorrectly treated as optional. - **Removed extra blank line** before class declarations in generated C# controller code (cosmetic). - New `WriteAccessor` setting to control property setter syntax (`set` vs `init`). - New `JsonLibraryVersion` setting for controlling enum attribute generation with System.Text.Json. - Fixed `MinLength` validation no longer incorrectly applied to DateTime properties. - Fixed nullable enum array detection for string enum converters. - Support for public fields with System.Text.Json when `IncludeFields` is enabled. For the full list of changes, see the [NJsonSchema v11.6.0 release notes](https://github.com/RicoSuter/NJsonSchema/releases/tag/v11.6.0). ## New Contributors * @ptasev made their first contribution in RicoSuter/NSwag#5320 * @MeikelLP made their first contribution in RicoSuter/NSwag#5337 * @adnanalbeda made their first contribution in RicoSuter/NSwag#5283 * @copyleftproducts made their first contribution in RicoSuter/NSwag#5315 * @Copilot made their first contribution in RicoSuter/NSwag#5348 **Full Changelog**: RicoSuter/NSwag@v14.6.3...v14.7.0 Commits viewable in [compare view](RicoSuter/NSwag@v14.6.3...v14.7.0). </details> Updated [NSwag.Core.Yaml](https://github.com/RicoSuter/NSwag) from 14.6.3 to 14.7.0. <details> <summary>Release notes</summary> _Sourced from [NSwag.Core.Yaml's releases](https://github.com/RicoSuter/NSwag/releases)._ ## 14.7.0 ## What's Changed * Upgrade to NUKE 10 by @lahma in RicoSuter/NSwag#5310 * Upgrade to NUKE 10.1.0 by @lahma in RicoSuter/NSwag#5314 * Fix specifying runtime for `NSwag.Npm` resulting in a `NConsole.UnusedArgumentException` by @ptasev in RicoSuter/NSwag#5320 * fix: nullpointer by @MeikelLP in RicoSuter/NSwag#5337 * fix axios template: [object Object] is not valid JSON. by @adnanalbeda in RicoSuter/NSwag#5283 * Fix legacy document transformation by @copyleftproducts in RicoSuter/NSwag#5315 * Fix compile error in generated C# client when media type contains quotes by @bkoelman in RicoSuter/NSwag#5345 * Fix duplicate client/operation names when operation IDs contain multiple underscores by @Copilot in RicoSuter/NSwag#5348 * Update to NJsonSchema v11.6.0 and Namotion.Reflection v3.5.0 (v14.7.0) by @RicoSuter in RicoSuter/NSwag#5357 ## NJsonSchema v11.6.0 (potentially breaking changes) This release updates to [NJsonSchema v11.6.0](https://github.com/RicoSuter/NJsonSchema/releases/tag/v11.6.0) and [Namotion.Reflection v3.5.0](https://github.com/RicoSuter/Namotion.Reflection/commits/master/), which include the following changes that may affect generated code: - **C# 11 `required` keyword now correctly recognized**: Properties using the C# 11 `required` keyword (via `RequiredMemberAttribute` / `JsonRequiredAttribute`) are now properly treated as required in the schema and generated code. Previously these were incorrectly treated as optional. - **Removed extra blank line** before class declarations in generated C# controller code (cosmetic). - New `WriteAccessor` setting to control property setter syntax (`set` vs `init`). - New `JsonLibraryVersion` setting for controlling enum attribute generation with System.Text.Json. - Fixed `MinLength` validation no longer incorrectly applied to DateTime properties. - Fixed nullable enum array detection for string enum converters. - Support for public fields with System.Text.Json when `IncludeFields` is enabled. For the full list of changes, see the [NJsonSchema v11.6.0 release notes](https://github.com/RicoSuter/NJsonSchema/releases/tag/v11.6.0). ## New Contributors * @ptasev made their first contribution in RicoSuter/NSwag#5320 * @MeikelLP made their first contribution in RicoSuter/NSwag#5337 * @adnanalbeda made their first contribution in RicoSuter/NSwag#5283 * @copyleftproducts made their first contribution in RicoSuter/NSwag#5315 * @Copilot made their first contribution in RicoSuter/NSwag#5348 **Full Changelog**: RicoSuter/NSwag@v14.6.3...v14.7.0 Commits viewable in [compare view](RicoSuter/NSwag@v14.6.3...v14.7.0). </details> Updated [NSwag.Generation.AspNetCore](https://github.com/RicoSuter/NSwag) from 14.6.3 to 14.7.0. <details> <summary>Release notes</summary> _Sourced from [NSwag.Generation.AspNetCore's releases](https://github.com/RicoSuter/NSwag/releases)._ ## 14.7.0 ## What's Changed * Upgrade to NUKE 10 by @lahma in RicoSuter/NSwag#5310 * Upgrade to NUKE 10.1.0 by @lahma in RicoSuter/NSwag#5314 * Fix specifying runtime for `NSwag.Npm` resulting in a `NConsole.UnusedArgumentException` by @ptasev in RicoSuter/NSwag#5320 * fix: nullpointer by @MeikelLP in RicoSuter/NSwag#5337 * fix axios template: [object Object] is not valid JSON. by @adnanalbeda in RicoSuter/NSwag#5283 * Fix legacy document transformation by @copyleftproducts in RicoSuter/NSwag#5315 * Fix compile error in generated C# client when media type contains quotes by @bkoelman in RicoSuter/NSwag#5345 * Fix duplicate client/operation names when operation IDs contain multiple underscores by @Copilot in RicoSuter/NSwag#5348 * Update to NJsonSchema v11.6.0 and Namotion.Reflection v3.5.0 (v14.7.0) by @RicoSuter in RicoSuter/NSwag#5357 ## NJsonSchema v11.6.0 (potentially breaking changes) This release updates to [NJsonSchema v11.6.0](https://github.com/RicoSuter/NJsonSchema/releases/tag/v11.6.0) and [Namotion.Reflection v3.5.0](https://github.com/RicoSuter/Namotion.Reflection/commits/master/), which include the following changes that may affect generated code: - **C# 11 `required` keyword now correctly recognized**: Properties using the C# 11 `required` keyword (via `RequiredMemberAttribute` / `JsonRequiredAttribute`) are now properly treated as required in the schema and generated code. Previously these were incorrectly treated as optional. - **Removed extra blank line** before class declarations in generated C# controller code (cosmetic). - New `WriteAccessor` setting to control property setter syntax (`set` vs `init`). - New `JsonLibraryVersion` setting for controlling enum attribute generation with System.Text.Json. - Fixed `MinLength` validation no longer incorrectly applied to DateTime properties. - Fixed nullable enum array detection for string enum converters. - Support for public fields with System.Text.Json when `IncludeFields` is enabled. For the full list of changes, see the [NJsonSchema v11.6.0 release notes](https://github.com/RicoSuter/NJsonSchema/releases/tag/v11.6.0). ## New Contributors * @ptasev made their first contribution in RicoSuter/NSwag#5320 * @MeikelLP made their first contribution in RicoSuter/NSwag#5337 * @adnanalbeda made their first contribution in RicoSuter/NSwag#5283 * @copyleftproducts made their first contribution in RicoSuter/NSwag#5315 * @Copilot made their first contribution in RicoSuter/NSwag#5348 **Full Changelog**: RicoSuter/NSwag@v14.6.3...v14.7.0 Commits viewable in [compare view](RicoSuter/NSwag@v14.6.3...v14.7.0). </details> Updated [NSwag.MSBuild](https://github.com/RicoSuter/NSwag) from 14.6.3 to 14.7.0. <details> <summary>Release notes</summary> _Sourced from [NSwag.MSBuild's releases](https://github.com/RicoSuter/NSwag/releases)._ ## 14.7.0 ## What's Changed * Upgrade to NUKE 10 by @lahma in RicoSuter/NSwag#5310 * Upgrade to NUKE 10.1.0 by @lahma in RicoSuter/NSwag#5314 * Fix specifying runtime for `NSwag.Npm` resulting in a `NConsole.UnusedArgumentException` by @ptasev in RicoSuter/NSwag#5320 * fix: nullpointer by @MeikelLP in RicoSuter/NSwag#5337 * fix axios template: [object Object] is not valid JSON. by @adnanalbeda in RicoSuter/NSwag#5283 * Fix legacy document transformation by @copyleftproducts in RicoSuter/NSwag#5315 * Fix compile error in generated C# client when media type contains quotes by @bkoelman in RicoSuter/NSwag#5345 * Fix duplicate client/operation names when operation IDs contain multiple underscores by @Copilot in RicoSuter/NSwag#5348 * Update to NJsonSchema v11.6.0 and Namotion.Reflection v3.5.0 (v14.7.0) by @RicoSuter in RicoSuter/NSwag#5357 ## NJsonSchema v11.6.0 (potentially breaking changes) This release updates to [NJsonSchema v11.6.0](https://github.com/RicoSuter/NJsonSchema/releases/tag/v11.6.0) and [Namotion.Reflection v3.5.0](https://github.com/RicoSuter/Namotion.Reflection/commits/master/), which include the following changes that may affect generated code: - **C# 11 `required` keyword now correctly recognized**: Properties using the C# 11 `required` keyword (via `RequiredMemberAttribute` / `JsonRequiredAttribute`) are now properly treated as required in the schema and generated code. Previously these were incorrectly treated as optional. - **Removed extra blank line** before class declarations in generated C# controller code (cosmetic). - New `WriteAccessor` setting to control property setter syntax (`set` vs `init`). - New `JsonLibraryVersion` setting for controlling enum attribute generation with System.Text.Json. - Fixed `MinLength` validation no longer incorrectly applied to DateTime properties. - Fixed nullable enum array detection for string enum converters. - Support for public fields with System.Text.Json when `IncludeFields` is enabled. For the full list of changes, see the [NJsonSchema v11.6.0 release notes](https://github.com/RicoSuter/NJsonSchema/releases/tag/v11.6.0). ## New Contributors * @ptasev made their first contribution in RicoSuter/NSwag#5320 * @MeikelLP made their first contribution in RicoSuter/NSwag#5337 * @adnanalbeda made their first contribution in RicoSuter/NSwag#5283 * @copyleftproducts made their first contribution in RicoSuter/NSwag#5315 * @Copilot made their first contribution in RicoSuter/NSwag#5348 **Full Changelog**: RicoSuter/NSwag@v14.6.3...v14.7.0 Commits viewable in [compare view](RicoSuter/NSwag@v14.6.3...v14.7.0). </details> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Eelco Los <5102501+EelcoLos@users.noreply.github.com>
MultipleClientsFromOperationIdOperationNameGeneratorused the second-to-last underscore segment as client name and last segment as operation name. Any two operation IDs sharing the same suffix (e.g.Orders_items_getandProducts_items_get) mapped to the same(client="items", operation="get")pair, producing duplicate generated classes and methods.Changes
GetClientName: switched from second-to-last segment to everything before the first_GetOperationName: switched fromLastIndexOf('_')toIndexOf('_')— returns everything after the first_OperationId_TestOperationId→TestOperationId→TestOrders_items_getitems→getOrders→items_getProducts_items_getitems→getProducts→items_get✅Since OpenAPI mandates globally unique operation IDs, splitting at the first
_guarantees that no two operation IDs can ever produce the same(client, operation)pair. The fix also propagates toMultipleClientsFromFirstTagAndOperationNameGenerator, which inheritsGetOperationNamefrom the base class.Tests
MultipleClientsFromOperationId(client, operation)pairsOriginal prompt
💡 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.