Fix system model base provider resolution - #11585
Merged
Wei Hu (live1206) merged 1 commit intoAug 11, 2026
Merged
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
commit: |
Contributor
|
No changes needing a change description found. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes base model provider resolution for SystemObjectModelProvider when the wrapped framework type has an existing CLR base type, ensuring the corresponding declared TypeSpec base model provider is created/resolved and registered for subsequent lookups.
Changes:
- Updates
ModelProvider.BuildBaseModelProvider()to fall back to creating the declared input base model provider when no mapped provider exists for the resolved CLR base type, and to register aCSharpTypeMapalias when the types match by name. - Adds a regression test covering automatic system base-provider creation across an input model hierarchy (
ArgumentException/SystemException).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs | Adds a fallback that creates the declared input base model provider and aliases the resolved CLR base CSharpType to it when they match by name. |
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/SystemObjectModelProviderTests.cs | Adds a regression test verifying base system providers are created/resolved from the input model inheritance chain. |
Contributor
Author
Arthur Ma (ArthurMa1978)
approved these changes
Aug 10, 2026
Jorge Rangel (jorgerangel-msft)
approved these changes
Aug 10, 2026
Wei Hu (live1206)
added a commit
to live1206/azure-sdk-for-net
that referenced
this pull request
Aug 11, 2026
Use the base generator build containing microsoft/typespec#11585 and remove the management generator fallback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e218381-df47-465e-bef4-9580c3c08bbd
pull Bot
pushed a commit
to jrcribb/cadl
that referenced
this pull request
Aug 14, 2026
…k types (microsoft#11678) Fixes microsoft#11676 ## Root cause Not PR microsoft#11585 (the auto-triage hypothesis in the issue). The real cause is framework-type resolution. `TypeFactory.CreateFrameworkType` ultimately falls back to `Type.GetType(fullyQualifiedTypeName)`. An unqualified `Type.GetType` only probes **corlib and the assembly that declares the calling method** — here `Microsoft.TypeSpec.Generator.dll`, which does not reference `System.ClientModel`. So `"System.ClientModel.FileBinaryContent"` always resolved to `null`. (This is the same reason `System.BinaryData`, `System.Uri`, `System.Text.Json.JsonElement` and `System.Net.IPAddress` already needed hard-coded special cases there.) Verified that this is not fixable by moving the call or by adding metadata references: ``` compile-time reference resolves: System.ClientModel.FileBinaryContent assembly loaded: System.ClientModel Type.GetType from referencing assembly: <null> Assembly.GetType on SCM assembly: System.ClientModel.FileBinaryContent ``` Even from an assembly that directly references *and has loaded* `System.ClientModel`, `Type.GetType` returns `null`. `CodeModelGenerator.AdditionalMetadataReferences` does not help either — that is a Roslyn/symbol-binding concern and has no effect on CLR reflection. ### How that produced the reported symptoms 1. `TypeSymbolExtensions.GetCSharpType` calls `CreateFrameworkType`, gets `null`, and builds a symbol-backed `CSharpType { Name = "FileBinaryContent", Namespace = "System.ClientModel", IsFrameworkType = false }`. 2. `ModelProvider.BuildProperties` back-compat handling sees `!lastContractPropertyType.Equals(outputProperty.Type)` (framework-backed vs symbol-backed), concludes the contract changed, and overwrites the property type with the symbol-backed look-alike. 3. `ScmModel.IsFileBinaryContentType` then returns `false`, so: - `BuildMultipartFileConstructors` returns `null` → the `string` / `Stream` / `BinaryData` convenience constructors disappear - the `[Experimental("SCME0004")]` attributes on the constructor, property and model-factory method are dropped, along with `using System.Diagnostics.CodeAnalysis;` - `MultipartFormDataSerializationDefinition.BuildScalarAdd` picks the model `Add<T>` overload instead of the `FileBinaryContent` one ### Why it looked intermittent The bad path only runs when a last contract is actually resolved (`SourceInputModel.FindForTypeInLastContract`). Runs without a last-contract assembly available never hit it, which is why byte-identical inputs produced different output across CI runs. The `dotnet msbuild` version delta noted in the issue is a proxy for that, not the cause. ## Fix Override `CreateFrameworkType` in `ScmTypeFactory` and fall back to `Assembly.GetType` scoped to the `System.ClientModel` assembly: ```csharp protected override Type? CreateFrameworkType(string fullyQualifiedTypeName) => base.CreateFrameworkType(fullyQualifiedTypeName) ?? typeof(BinaryContent).Assembly.GetType(fullyQualifiedTypeName); ``` This is deterministic (the assembly is a compile-time reference of `Microsoft.TypeSpec.Generator.ClientModel`) and fixes every `System.ClientModel` type, not just `FileBinaryContent`. Deliberately avoided broad probing such as `AppDomain.CurrentDomain.GetAssemblies()`, which would reintroduce load-order nondeterminism — exactly the class of problem this issue is about. ## Tests Added `TestMultipartFormDataModel_LastContractFileType_KeepsFileBinaryContentFrameworkType`, which loads a last contract declaring `public FileBinaryContent ProfileImage { get; }` and asserts the property stays a framework type, that `IsFileBinaryContentType` recognizes it, and that the emitted model is byte-identical to the no-last-contract output. It fails on `main` with `Expected: True But was: False` and passes with this change. ## Validation - `Microsoft.TypeSpec.Generator.Tests` — 1895/1895 passed - `Microsoft.TypeSpec.Generator.ClientModel.Tests` — 1571/1571 passed - `eng/scripts/Generate.ps1` — regenerated all libraries, no output drift - `npm run cop` — `cop checks passed.` ## Follow-up `Azure.Generator` in `Azure/azure-sdk-for-net` derives from `ScmTypeFactory`, so it picks up `System.ClientModel` resolution from this change automatically. It will still need the equivalent one-line fallback for its own `Azure.Core` / `Azure.ResourceManager` types. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 33840ead-d95e-4c5d-91eb-8765e25089bc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ensure MTG creates and resolves the corresponding input base model provider when a
SystemObjectModelProviderwraps a framework type with an existing CLR base type.SystemObjectModelProvider.BuildBaseType()prefersSystemType.BaseType, so the normalModelProvider.BuildBaseType()path does not create the declared TypeSpec base model.BuildBaseModelProvider()previously only checkedCSharpTypeMap, which could leave the base providernulldepending on model creation order.The fallback now:
CSharpTypealias for subsequent lookups.Added a regression test using
ArgumentExceptionandSystemExceptionto cover both automatic system base-provider creation and framework/non-frameworkCSharpTypeequivalence.Contributes to #10787.
Validation
SystemObjectModelProviderTests: 27 passednpm run test:generatorMgmt-TypeSpec-MultiService