Skip to content

[Mgmt Generator] Multi-scope resource generates duplicate GetXxxResource factory methods (CS0111) #57645

@mzhongl524

Description

@mzhongl524

Bug Description

The MPG generator (Azure Management Plane Generator) produces duplicate identical GetXxxResource factory methods when a TypeSpec spec defines a multi-scope resource using Legacy.ExtensionOperations with multiple interfaces. This causes CS0111 ("Type already defines a member with the same parameter types") compilation errors.

Spec Structure

Repo: Azure/azure-rest-api-specs
Path: specification/kubernetesconfiguration/resource-manager/Microsoft.KubernetesConfiguration/extensionTypes/
Commit: 8d0a948edb8a860ea00768533210f98634a0870e

ExtensionType.tsp

Defines the ExtensionType resource model with 2 interfaces at different scopes:

// Scope 1: Subscription/Location
// Path: /subscriptions/{subscriptionId}/providers/Microsoft.KubernetesConfiguration/locations/{location}/extensionTypes/{extensionTypeName}
alias ExtensionTypeOps = Azure.ResourceManager.Legacy.ExtensionOperations<
  { ...ApiVersionParameter; ...SubscriptionIdParameter; @path @segment("providers") @key providerNamespace: "Microsoft.KubernetesConfiguration"; ...LocationParameter; },
  {},
  { @path @segment("extensionTypes") @key @pattern("^[a-zA-Z][a-zA-Z0-9-_]*$") extensionTypeName: string; }
>;
@armResourceOperations(#{ omitTags: true })
interface ExtensionTypes { ... }

// Scope 2: ResourceGroup/Cluster
// Path: /subscriptions/{subscriptionId}/resourceGroups/{rg}/providers/{clusterRp}/{clusterResourceName}/{clusterName}/providers/Microsoft.KubernetesConfiguration/extensionTypes/{extensionTypeName}
alias ExtensionTypeOperationGroupOps = Azure.ResourceManager.Legacy.ExtensionOperations<
  { ...ApiVersionParameter; ...SubscriptionIdParameter; ...ResourceGroupParameter; @path @segment("providers") @key clusterRp: string; @key @path clusterResourceName: string; @key @path clusterName: string; },
  { ...Extension.ExtensionProviderNamespace<ExtensionType>; ...ParentKeysOf<ExtensionType>; },
  { ...Extension.ExtensionProviderNamespace<ExtensionType>; ...KeysOf<ExtensionType>; }
>;
@armResourceOperations(#{ omitTags: true })
interface ExtensionTypeOperationGroup { ... }

ExtensionTypeVersionForReleaseTrain.tsp

Defines a child resource @parentResource(ExtensionType) with another 2 interfaces at different scopes:

interface ExtensionTypeVersionForReleaseTrains { ... }          // Location scope
interface ExtensionTypeVersionForReleaseTrainOperationGroup { ... }  // Cluster scope

back-compatible.tsp

Uses @@clientLocation to consolidate operations:

@@clientLocation(ExtensionTypeOperationGroup.get, ExtensionTypes);
@@clientLocation(ExtensionTypeOperationGroup.list, ExtensionTypes);
@@clientLocation(ExtensionTypeVersionForReleaseTrains.getVersion, ExtensionTypes);
@@clientLocation(ExtensionTypeVersionForReleaseTrains.listVersions, ExtensionTypes);
@@clientLocation(ExtensionTypeVersionForReleaseTrainOperationGroup.clusterGetVersion, ExtensionTypes);
@@clientLocation(ExtensionTypeVersionForReleaseTrainOperationGroup.clusterListVersions, ExtensionTypes);

But @@clientLocation only moves operations (Read/List), not the resource factory methods.

Swagger (Generated OpenAPI)

The swagger produces 4 operation groups that all reference the same ExtensionType schema:

  • ExtensionTypes_LocationGet / ExtensionTypes_LocationList (subscription/location scope)
  • ExtensionTypeOperationGroup_Get / ExtensionTypeOperationGroup_List (cluster scope)
  • ExtensionTypeVersionForReleaseTrains_GetVersion / ..._ListVersions (location scope)
  • ExtensionTypeVersionForReleaseTrainOperationGroup_ClusterGetVersion / ..._ClusterListVersions (cluster scope)

All operation groups share the same resource model ExtensionType, which maps to the single C# class ExtensionTypeInterfaceResource.

Generated Code (Bug)

After running dotnet build /t:GenerateCode, the generator produces 4 identical copies of each factory method:

In KubernetesConfigurationExtensionTypesExtensions.cs:

// Copy 1 (from ExtensionTypes interface)
public static ExtensionTypeInterfaceResource GetExtensionTypeInterfaceResource(this ArmClient client, ResourceIdentifier id) { ... }
// Copy 2 (from ExtensionTypeOperationGroup interface)
public static ExtensionTypeInterfaceResource GetExtensionTypeInterfaceResource(this ArmClient client, ResourceIdentifier id) { ... }
// Copy 3 (from ExtensionTypeVersionForReleaseTrains interface)
public static ExtensionTypeInterfaceResource GetExtensionTypeInterfaceResource(this ArmClient client, ResourceIdentifier id) { ... }
// Copy 4 (from ExtensionTypeVersionForReleaseTrainOperationGroup interface)
public static ExtensionTypeInterfaceResource GetExtensionTypeInterfaceResource(this ArmClient client, ResourceIdentifier id) { ... }

In MockableKubernetesConfigurationExtensionTypesArmClient.cs:

// Same 4 identical copies of:
public virtual ExtensionTypeInterfaceResource GetExtensionTypeInterfaceResource(ResourceIdentifier id) { ... }

Expected Behavior

The generator should deduplicate GetXxxResource factory methods. Regardless of how many interfaces/scopes reference the same resource model, only one copy of each factory method should be generated.

Current Workaround

SDK-side workaround using [CodeGenSuppress] + custom partial class implementation:

[CodeGenSuppress("GetExtensionTypeInterfaceResource", typeof(ArmClient), typeof(ResourceIdentifier))]
public static partial class KubernetesConfigurationExtensionTypesExtensions
{
    public static ExtensionTypeInterfaceResource GetExtensionTypeInterfaceResource(this ArmClient client, ResourceIdentifier id) { ... }
}

[CodeGenSuppress("GetExtensionTypeInterfaceResource", typeof(ResourceIdentifier))]
public partial class MockableKubernetesConfigurationExtensionTypesArmClient
{
    public virtual ExtensionTypeInterfaceResource GetExtensionTypeInterfaceResource(ResourceIdentifier id) { ... }
}

Affected Component

eng/packages/http-client-csharp-mgmt — resource factory method generation logic. The deduplication should likely happen when collecting resource factory methods across multiple interfaces/scopes in resolve-arm-resources-converter.ts or the extension method generation code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    CodeGenIssues that relate to code generationMgmtThis issue is related to a management package.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions