diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs index de172f444a8..0b84b814211 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs @@ -291,7 +291,13 @@ private static bool IsDiscriminator(InputProperty property) return property is InputModelProperty modelProperty && modelProperty.IsDiscriminator; } - private ModelProvider? BuildBaseModelProvider() + /// + /// Builds the representing the base model of this model. + /// Emitters that override to redirect the generated C# base + /// class should also override this method so that and + /// stay consistent. + /// + protected virtual ModelProvider? BuildBaseModelProvider() { // consider models that have been customized to inherit from a different generated model if (CustomCodeView?.BaseType != null) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs index 7c8e88e49d1..663ccd88e28 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs @@ -422,6 +422,45 @@ public void BuildBaseType() Assert.AreEqual(baseModel!.Type, derivedModel!.Type.BaseType); } + // Verifies an emitter can override BuildBaseType and BuildBaseModelProvider together to + // redirect the C# base class to a custom ModelProvider, keeping BaseType and + // BaseModelProvider in agreement. + [Test] + public void OverridingBuildBaseTypeAndBuildBaseModelProvider_KeepsBaseTypeConsistent() + { + var inputBase = InputFactory.Model("baseModel", usage: InputModelTypeUsage.Input, properties: []); + var inputDerived = InputFactory.Model("derivedModel", usage: InputModelTypeUsage.Input, properties: [], baseModel: inputBase); + CodeModelGenerator.Instance.TypeFactory.CreateModel(inputBase); + + var customBaseProvider = new CustomBaseModelProvider(inputBase); + var derivedProvider = new BuildBaseTypeOverridingModelProvider(inputDerived, customBaseProvider); + + Assert.AreEqual(customBaseProvider.Type, derivedProvider.BaseType); + Assert.AreSame(customBaseProvider, derivedProvider.BaseModelProvider); + } + + private class CustomBaseModelProvider : ModelProvider + { + public CustomBaseModelProvider(InputModelType inputModel) : base(inputModel) { } + + protected override string BuildName() => "CustomBase"; + } + + private class BuildBaseTypeOverridingModelProvider : ModelProvider + { + private readonly ModelProvider _redirectedBaseProvider; + + public BuildBaseTypeOverridingModelProvider(InputModelType inputModel, ModelProvider redirectedBaseProvider) : base(inputModel) + { + _redirectedBaseProvider = redirectedBaseProvider; + } + + protected override CSharpType? BuildBaseType() => _redirectedBaseProvider?.Type; + + // The emitter overrides BuildBaseModelProvider to keep it in sync with the redirected BaseType. + protected override ModelProvider? BuildBaseModelProvider() => _redirectedBaseProvider; + } + [Test] public void BuildModelAsStruct() {