Skip to content

Add SystemObjectModelProvider for non-generated base type customization#9862

Open
live1206 wants to merge 18 commits into
microsoft:mainfrom
live1206:feature/system-object-model-provider
Open

Add SystemObjectModelProvider for non-generated base type customization#9862
live1206 wants to merge 18 commits into
microsoft:mainfrom
live1206:feature/system-object-model-provider

Conversation

@live1206
Copy link
Copy Markdown
Contributor

@live1206 live1206 commented Mar 2, 2026

Summary

Fixes #9234

Adds SystemObjectModelProvider, a ModelProvider subclass for input models that map to existing framework/system model types such as TrackedResourceData and ResourceData. This lets downstream generators, especially the Azure Management generator, use non-generated framework types as model bases while still allowing the base generator to reason about inherited properties, constructors, and serialization.

Key changes

SystemObjectModelProvider

  • Represents a framework/system model type using a CSharpType plus the InputModelType it replaces.
  • Extends ModelProvider, unlike SystemObjectTypeProvider, so it can be used as BaseModelProvider for generated derived models.
  • Exposes SystemType and CrossLanguageDefinitionId for downstream generator logic.
  • Uses the framework type name/namespace and never writes a generated file.
  • Builds properties and constructors from the input model so derived models can correctly pass inherited constructor parameters such as id, name, resourceType, systemData, tags, and location.
  • Does not generate fields, serialization providers, or a raw data field because the framework type owns those implementation details.

Model inheritance handling

  • ModelProvider.BuildBaseModelProvider() now resolves customized base types through BaseType, including generated-model customizations and a name+namespace fallback for framework/non-framework CSharpType equality differences.
  • ModelProvider.Update(...) now accepts baseModelProvider, replacing the earlier dedicated setter API and resetting cached members when the base changes.
  • Property generation deduplicates against a SystemObjectModelProvider base chain, so derived generated models do not re-emit framework-owned properties.
  • Raw-data and serialization behavior now accounts for SystemObjectModelProvider bases.

Constructor initializer fix

  • Fixed base constructor initializer argument selection after public input parameters are substituted. This prevents generated code like : base(location0) when the actual constructor parameter is location.

Motivation

The Azure Management generator has ARM resource models that inherit from framework types such as TrackedResourceData and ResourceData. Previously it needed a local InheritableSystemObjectModelProvider with custom constructor and property handling. Moving this capability into the shared generator lets downstream generators reuse normal ModelProvider behavior for inheritance, property deduplication, serialization modifiers, and constructor generation.

Validation

  • npm run build:generator in packages/http-client-csharp passes.
  • Validated with the companion Azure SDK PR using the local TypeSpec checkout:
    • eng/packages/http-client-csharp-mgmt/eng/scripts/Generate.ps1 completed successfully.
    • dotnet test eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/test/Azure.Generator.Mgmt.Tests.csproj passed: 194/194 tests.

Companion PR

live1206 and others added 2 commits March 2, 2026 16:56
…ed fallback

Add SystemObjectModelProvider extending ModelProvider for downstream generators
that map input models to existing framework types (e.g., ARM Resource to
ResourceData). Unlike SystemObjectTypeProvider which extends TypeProvider,
this class can serve as BaseModelProvider for derived models.

Fix BuildBaseModelProvider() to use name+namespace based CSharpTypeMap fallback
when strict CSharpType equality fails. This resolves the case where custom code
overrides a base type to an external type (e.g., TrackedResourceData) but the
CSharpTypeMap lookup fails due to framework vs non-framework CSharpType mismatch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ObjectModelProvider

- Added CrossLanguageDefinitionId property (from input model) to support downstream
  generators that need to check the cross-language definition ID
- Fixed BuildName() null safety: _systemType may be null when called from base
  constructor before field assignment (same pattern as BuildNamespace)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp label Mar 2, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Mar 2, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-client-csharp@9862

commit: 8239c8d

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 2, 2026

No changes needing a change description found.

live1206 added a commit to live1206/azure-sdk-for-net that referenced this pull request Mar 2, 2026
…ovider from upstream

Use SystemObjectModelProvider from Microsoft.TypeSpec.Generator instead of the local
InheritableSystemObjectModelProvider. This aligns with the upstream PR
microsoft/typespec#9862 which adds SystemObjectModelProvider to the base generator.

- ManagementTypeFactory: create SystemObjectModelProvider(replacedType, model)
- InheritableSystemObjectModelVisitor: swap all type checks and property access
- NameVisitor: use SystemObjectModelProvider pattern match
- ManagementOutputLibrary: filter SystemObjectModelProvider

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
live1206 and others added 5 commits March 2, 2026 18:20
…zation, and constructors

- SystemObjectModelProvider now returns empty for BuildProperties, BuildFields,
  BuildSerializationProviders, BuildConstructors, and null for BuildRawDataField
- ModelProvider.BuildProperties skips base properties when base chain includes
  SystemObjectModelProvider
- ModelProvider.BuildRawDataField changed to protected virtual
- MrwSerializationTypeDefinition uses virtual (not override) for create/persist
  methods when base is SystemObjectModelProvider

This enables downstream generators (mgmt) to handle framework type bases
natively without post-processing visitors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tests demonstrate capabilities that SystemObjectModelProvider provides that are
missing from SystemObjectTypeProvider:

1. Type hierarchy: SystemObjectModelProvider extends ModelProvider (not TypeProvider),
   so it can serve as BaseModelProvider for derived models
2. Property deduplication: derived models skip properties defined in framework base
3. Raw data field: SystemObjectModelProvider returns null, derived models create own
4. Empty members: no generated properties/fields/constructors/serialization
5. Serialization modifiers: correct virtual vs override for 4 serialization methods
6. Name/namespace from system CSharpType

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…se property dedup from SystemObjectModelProvider chain

- SetBaseModelProvider: public method allowing visitors to replace a model's base
- Reset override: clears rawDataField, additionalPropertyFields/Properties, fullConstructor
- InputModel property: exposes the private _inputModel field for external use
- BuildProperties: when base is SystemObjectModelProvider, also includes base properties
  from its InputModel chain for proper dedup when custom code overrides base type

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove BuildProperties() => [] and BuildConstructors() => [] overrides so
that SystemObjectModelProvider builds properties and constructors from its
input model. This allows derived models to correctly include base class
parameters (e.g., id, name, resourceType, systemData, tags, location) in
their constructors and generate proper :base() initializer calls.

Without this fix, derived models lose all base class constructor parameters
because the empty overrides make the base model invisible to the constructor
building logic in ModelProvider.BuildConstructorParameters().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
live1206 and others added 2 commits March 5, 2026 16:10
Access _inputModel directly in BuildProperties() since private members
are accessible within the declaring class on any instance. This avoids
adding unnecessary public API surface.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fold base model provider changes into ModelProvider.Update() instead of a
separate public SetBaseModelProvider() method. This follows the existing
Update() pattern and automatically resets dependent members when the base
model changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the stale Mark a PR that hasn't been recently updated and will be closed. label Apr 4, 2026
@live1206 live1206 removed the stale Mark a PR that hasn't been recently updated and will be closed. label May 9, 2026
live1206 and others added 3 commits May 11, 2026 15:35
…-model-provider

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…er' into feature/system-object-model-provider
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
live1206 and others added 2 commits May 12, 2026 12:09
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
{
outputProperty.Modifiers |= MethodSignatureModifiers.New;
var fieldName = $"_{baseProperty.Name.ToVariableName()}";
var fieldName = $"_{baseProperty.Property.Name.ToVariableName()}";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are there so many changes here? Can we also get a regen preview please?

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unable to customize base type of generated model to a non-generated type

3 participants