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..d5f3cc204af 100644 --- a/packages/http-server-csharp/src/lib/service.ts +++ b/packages/http-server-csharp/src/lib/service.ts @@ -214,6 +214,15 @@ 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; + arrayFile.meta["skipEmit"] = true; + 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); } @@ -1265,6 +1274,10 @@ export async function $onEmit(context: EmitContext) return sourceFile.meta.emitted; } + if (sourceFile.meta["skipEmit"]) { + return { path: sourceFile.path, contents: "" }; + } + const emittedSourceFile: EmittedSourceFile = { path: sourceFile.path, contents: "", @@ -1426,7 +1439,7 @@ export async function $onEmit(context: EmitContext) break; } } - return super.writeOutput(emittedSourceFiles); + return super.writeOutput(emittedSourceFiles.filter((f) => !f.meta["skipEmit"])); } } diff --git a/packages/http-server-csharp/test/generation.test.ts b/packages/http-server-csharp/test/generation.test.ts index 5cb6dfacfca..ab3d989aedb 100644 --- a/packages/http-server-csharp/test/generation.test.ts +++ b/packages/http-server-csharp/test/generation.test.ts @@ -48,6 +48,11 @@ function assertFileDoesNotContain( ); } +function assertFileNotEmitted(fs: TestFileSystem, fileName: string): void { + const result = [...fs.fs.entries()].filter((e) => e[0].includes(`/${fileName}`)); + assert.strictEqual(result.length, 0, `Expected ${fileName} to not be emitted, but it was`); +} + async function compileAndValidateSingleModel( tester: TesterInstance, code: string, @@ -3396,6 +3401,61 @@ describe("collection type: defined as emitter option", () => { }); }); +describe("arrayDeclarationContext", () => { + it("emits correct types for array model declarations", async () => { + const fs = await compileAndValidateMultiple( + tester, + ` + model Tags is Array; + @route("/tags") @get op getTags(): Tags; + `, + [["IContosoOperations.cs", ["Task GetTagsAsync( )"]]], + ); + assertFileNotEmitted(fs, "Tags.cs"); + }); + + it("emits correct types for array model with custom namespace", async () => { + const fs = await compileAndValidateMultiple( + tester, + [ + ` + model Items is Array; + @route("/items") @get op getItems(): Items; + `, + "My.Custom.Ns", + ], + [["INsOperations.cs", ["Task GetItemsAsync( )"]]], + ); + assertFileNotEmitted(fs, "Items.cs"); + }); + + it("emits correct types for array model with complex element type", async () => { + const fs = 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; }", + ], + ], + ["IContosoOperations.cs", ["Task GetWidgetsAsync( )"]], + ], + ); + assertFileNotEmitted(fs, "WidgetList.cs"); + }); +}); + it("emits class for model extending another model with no additional properties", async () => { await compileAndValidateMultiple( tester,