Skip to content

[http-client-csharp] Model factory loses namespaces for custom constructor parameter types #11846

Description

@live1206

Description

When a model's generated full constructor is suppressed and replaced by a custom internal constructor, ModelFactoryProvider can generate a factory overload whose custom model parameter types have an empty namespace.

The resulting C# is invalid:

using ;

public static EffectiveNetworkSecurityGroup EffectiveNetworkSecurityGroup(
    global::.NetworkSubResource networkSecurityGroup = default,
    global::.EffectiveNetworkSecurityGroupAssociation association = default,
    IEnumerable<global::.EffectiveNetworkSecurityRule> effectiveSecurityRules = default,
    string tagMap = default)

This was reproduced in Azure.ResourceManager.Network during the full management-plane regeneration in Azure/azure-sdk-for-net#62505.

Versions

  • @azure-typespec/http-client-csharp-mgmt: 1.0.0-alpha.20260902.1
  • @azure-typespec/http-client-csharp: 1.0.0-alpha.20260828.4
  • @typespec/http-client-csharp: 1.0.0-alpha.20260828.11
  • Microsoft.TypeSpec.Generator*: 1.0.0-alpha.20260828.11

Reproduction shape

The model has a custom internal compatibility constructor because its generated full constructor is suppressed:

[CodeGenSuppress(
    "EffectiveNetworkSecurityGroup",
    typeof(NetworkSubResource),
    typeof(EffectiveNetworkSecurityGroupAssociation),
    typeof(IReadOnlyList<EffectiveNetworkSecurityRule>),
    typeof(string),
    typeof(IDictionary<string, BinaryData>))]
public partial class EffectiveNetworkSecurityGroup
{
    internal EffectiveNetworkSecurityGroup(
        NetworkSubResource networkSecurityGroup,
        EffectiveNetworkSecurityGroupAssociation association,
        IReadOnlyList<EffectiveNetworkSecurityRule> effectiveSecurityRules,
        string tagMap,
        IDictionary<string, BinaryData> additionalBinaryDataProperties)
    {
        // Compatibility implementation.
    }
}

The project also contains a valid custom model-factory overload with a different compatibility signature, so it does not suppress the newly generated overload.

Run the local management regeneration:

pwsh eng/packages/http-client-csharp-mgmt/eng/scripts/RegenSdkLocal.ps1 `
  -Services Azure.ResourceManager.Network `
  -Parallel 1

Root cause

ModelFactoryProvider.GetBinaryDataParamAndFullCtorForFactoryMethod intentionally selects an internal constructor from modelProvider.CanonicalView.Constructors when the generated full constructor was suppressed.

GetParameters then calls GetModelFactoryParam, which uses:

parameter.Type.InputType

For generated parameters, InputType maps back to the input model correctly. For Roslyn-derived custom-constructor model parameters, that association is unavailable and InputType has the model name but an empty namespace. The writer consequently emits global::.Type, and namespace collection emits using ;.

Expected behavior

Model-factory generation should either:

  1. preserve or correctly resolve the custom constructor parameter's C# type and namespace;
  2. use InputType only when a valid input association exists; or
  3. skip the generated factory overload with an actionable diagnostic when the selected custom constructor cannot be represented safely.

It must never emit an empty namespace, global::.Type, or using ;.

Suggested regression coverage

Add a model-factory test with:

  • a suppressed generated full constructor;
  • a custom internal replacement constructor;
  • custom constructor parameters that reference generated model types;
  • model-factory generation enabled.

Assert that all generated parameter types retain their namespaces and that output contains neither global::. nor using ;.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    emitter:client:csharpIssue for the C# client emitter: @typespec/http-client-csharp

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions