microsoft / microsoft/typespec

C# generator should fully support inheritable system model replacements

Open
#10,787 3 comments 0 reactions 1 assignee Claimed by @live1206 View on GitHub
emitter:client:csharp
Dominant language
Java
Stars
5.9k
Forks
394
Avg merge
1d 23h
Merged PRs (30d)
104

Description

## Description

The C# generator has `SystemObjectModelProvider`, which is a useful building block for mapping an input model to an existing framework/system model while still allowing derived generated models to use it as `BaseModelProvider`. However, Azure management-plane generation still needs a custom MPG visitor (`InheritableSystemObjectModelVisitor`) to repair several behaviors after mapping ARM common types like `Resource`, `ProxyResource`, and `TrackedResource` to framework types such as `ResourceData` and `TrackedResourceData`.

We would like MTG/ClientModel to provide first-class support for inheritable system model replacement so MPG can remove or significantly shrink this visitor.

## Current workaround in MPG

In Azure/azure-sdk-for-net#56634, MPG uses `InheritableSystemObjectModelVisitor` to:

1. Register framework `CSharpType` aliases in `CSharpTypeMap` so custom-code base types like `TrackedResourceData` resolve back to the mapped `SystemObjectModelProvider`.
2. Repair `SystemObjectModelProvider` namespace after generic namespace visitors run.
3. Remove model factory methods returning known framework management types.
4. Manually remove inherited properties from derived models, including wire-name matches such as a derived `Name0` property whose serialized name is `name`, because the framework base already owns `Name`.
5. Strip orphaned `virtual` modifiers after removing inherited duplicate properties.

## Gap in MTG today

`SystemObjectModelProvider : ModelProvider` already helps with some inheritance behavior:

- It can serve as `BaseModelProvider`.
- It sets `ShouldSkipDerivedModelProperties = true`.
- It does not emit fields or serialization providers for the framework type.
- `ModelProvider.BuildProperties()` skips derived properties whose C# property name matches a base property from a provider marked `ShouldSkipDerivedModelProperties`.

But this is not enough for ARM-style common types:

### 1. Wire-name based property dedup is missing

MTG skips inherited properties by C# property name. MPG also needs wire-name equivalence.

Example:

- Framework base has `Name` with wire name `name`.
- Derived input model has a generated C# property `Name0` but serialized/wire name `name`.
- The derived property should be removed because the framework base already represents that wire property.

Without this, generated output can duplicate ARM base properties as renamed properties like `Name0`.

### 2. Framework/non-framework `CSharpType` alias lookup is fragile

Custom code can resolve a base type like `TrackedResourceData` as a framework `CSharpType`, while the mapped system provider may be keyed differently. MTG should make the system replacement discoverable under all equivalent `CSharpType` forms, or compare framework/system replacement types consistently in `BuildBaseModelProvider()`.

### 3. Namespace rewriting should not corrupt system providers

Generic namespace visitors can rewrite provider namespaces to the SDK namespace. A system/framework provider should keep its framework namespace or otherwise be immune to normal generated-model namespace rewriting.

### 4. Model factory methods should not be emitted for framework replacements

If an input model is replaced by a framework/system type, model factory methods returning that framework replacement should be skipped by default. MPG currently filters them out.

### 5. Constructor/field/serialization behavior should fully account for system bases

Once a generator maps an input model to an inheritable framework base, derived models should not duplicate inherited fields/properties/constructor parameters/serialization overrides, including the wire-name equivalence case above.

## Desired behavior

A generator should be able to register something equivalent to:

```csharp
Known ARM model "TrackedResource" -> framework type TrackedResourceData
Known ARM model "Resource" -> framework type ResourceData
```

Then MTG should handle derived generated models correctly without a management-plane visitor manually walking and editing properties.

## Suggested test case

Add a ClientModel/MTG test with:

- A system base input model representing `ResourceData` or `TrackedResourceData`, with properties like `id`, `name`, `type`, `tags`, `location`.
- A derived generated model with a property named `name0` whose serialized name is `name`, plus a normal service-specific property.
- The base model mapped to a `SystemObjectModelProvider`/framework type.

Expected:

- The derived generated model does not emit `Name0`.
- It does not duplicate base fields or constructor parameters.
- Serialization methods call/override appropriately for a system base.
- Model factory methods for the framework replacement type are not generated.
- The system provider keeps the framework namespace.

## Context

This is needed to simplify Azure management-plane generation and eventually remove more of `InheritableSystemObjectModelVisitor` from MPG.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.