microsoft / microsoft/typespec
C# generator should fully support inheritable system model replacements
- 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
Assessment
This issue has not been assessed yet.