diff --git a/.chronus/changes/sramsey-csharp-service-arrayDeclarationContext-2026-3-9-13-37-52.md b/.chronus/changes/sramsey-csharp-service-arrayDeclarationContext-2026-3-9-13-37-52.md new file mode 100644 index 00000000000..807cfda1846 --- /dev/null +++ b/.chronus/changes/sramsey-csharp-service-arrayDeclarationContext-2026-3-9-13-37-52.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/http-server-csharp" +--- + +add arrayDeclarationContext \ No newline at end of file diff --git a/packages/http-server-csharp/src/lib/service.ts b/packages/http-server-csharp/src/lib/service.ts index efa2d09a9c3..a3f0fbb35b5 100644 --- a/packages/http-server-csharp/src/lib/service.ts +++ b/packages/http-server-csharp/src/lib/service.ts @@ -214,6 +214,14 @@ export async function $onEmit(context: EmitContext) return "TypeSpec.Service"; } + arrayDeclarationContext(array: Model, name: string, elementType: Type) { + const arrayName = ensureCSharpIdentifier(this.emitter.getProgram(), array, name); + const arrayFile = this.emitter.createSourceFile(`generated/models/${arrayName}.cs`); + arrayFile.meta[this.#sourceTypeKey] = CSharpSourceType.Model; + const arrayNamespace = this.#getOrAddNamespace(array.namespace); + return this.#createModelContext(arrayNamespace, arrayFile, arrayName); + } + arrayDeclaration(array: Model, name: string, elementType: Type): EmitterOutput { return this.collectionDeclaration(elementType, array); } diff --git a/packages/http-server-csharp/test/generation.test.ts b/packages/http-server-csharp/test/generation.test.ts index 5cb6dfacfca..9e6caf5a629 100644 --- a/packages/http-server-csharp/test/generation.test.ts +++ b/packages/http-server-csharp/test/generation.test.ts @@ -3396,6 +3396,109 @@ describe("collection type: defined as emitter option", () => { }); }); +describe("arrayDeclarationContext", () => { + it("generates a dedicated file for array model declarations", async () => { + await compileAndValidateMultiple( + tester, + ` + model Tags is Array; + @route("/tags") @get op getTags(): Tags; + `, + [ + [ + "Tags.cs", + [ + "// Generated by @typespec/http-server-csharp", + "using System;", + "using System.Text.Json;", + "using System.Text.Json.Serialization;", + "using TypeSpec.Helpers.JsonConverters;", + "using TypeSpec.Helpers;", + ], + ], + ["IContosoOperations.cs", ["Task GetTagsAsync( )"]], + ], + ); + }); + + it("generates a dedicated file for array model with custom namespace", async () => { + await compileAndValidateMultiple( + tester, + [ + ` + model Items is Array; + @route("/items") @get op getItems(): Items; + `, + "My.Custom.Ns", + ], + [ + ["Items.cs", ["// Generated by @typespec/http-server-csharp", "using System;"]], + ["INsOperations.cs", ["Task GetItemsAsync( )"]], + ], + ); + }); + + it("generates a dedicated file for array model with complex element type", async () => { + await compileAndValidateMultiple( + tester, + ` + model Widget { + id: int32; + name: string; + } + model WidgetList is Array; + @route("/widgets") @get op getWidgets(): WidgetList; + `, + [ + [ + "Widget.cs", + [ + "public partial class Widget", + "public int Id { get; set; }", + "public string Name { get; set; }", + ], + ], + ["WidgetList.cs", ["// Generated by @typespec/http-server-csharp", "using System;"]], + ["IContosoOperations.cs", ["Task GetWidgetsAsync( )"]], + ], + ); + }); + + it("generates a dedicated file for named array with union element type", async () => { + await compileAndValidateMultiple( + tester, + ` + model ToolCall { + id: string; + type: "function"; + name: string; + } + + model CustomToolCall { + id: string; + type: "custom"; + payload: string; + } + + /** The tool calls generated by the model, such as function calls. */ + model ToolCalls is (ToolCall | CustomToolCall)[]; + + model AssistantMessage { + role: "assistant"; + tool_calls?: ToolCalls; + } + + @route("/chat") op chat(): AssistantMessage; + `, + [ + ["AssistantMessage.cs", [`public string Role { get; } = "assistant";`]], + ["ToolCalls.cs", ["// Generated by @typespec/http-server-csharp"]], + ["IContosoOperations.cs", ["Task ChatAsync( );"]], + ], + ); + }); +}); + it("emits class for model extending another model with no additional properties", async () => { await compileAndValidateMultiple( tester,