diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs index 7089a0d2e4a..928af9ef4f9 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs @@ -380,7 +380,7 @@ private ValueExpression GetResultConversion(ClientResponseApi result, HttpRespon } else { - return response.Content().ToObjectFromJson(responseBodyType.OutputType); + return ModelReaderWriterSnippets.Read(response.Content(), responseBodyType.OutputType); } } if (responseBodyType.IsDictionary) @@ -391,7 +391,7 @@ private ValueExpression GetResultConversion(ClientResponseApi result, HttpRespon } else { - return response.Content().ToObjectFromJson(responseBodyType.OutputType); + return ModelReaderWriterSnippets.Read(response.Content(), responseBodyType.OutputType); } } if (responseBodyType.Equals(typeof(string)) && ServiceMethod.Operation.Responses.Any(r => r.IsErrorResponse is false && r.ContentTypes.Contains("text/plain"))) @@ -400,11 +400,11 @@ private ValueExpression GetResultConversion(ClientResponseApi result, HttpRespon } if (responseBodyType.IsFrameworkType) { - return response.Content().ToObjectFromJson(responseBodyType); + return ModelReaderWriterSnippets.Read(response.Content(), responseBodyType); } if (responseBodyType.IsEnum) { - return responseBodyType.ToEnum(response.Content().ToObjectFromJson(responseBodyType.UnderlyingEnumType)); + return responseBodyType.ToEnum(ModelReaderWriterSnippets.Read(response.Content(), responseBodyType.UnderlyingEnumType)); } return result.CastTo(responseBodyType); } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Snippets/ModelReaderWriterSnippets.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Snippets/ModelReaderWriterSnippets.cs new file mode 100644 index 00000000000..3542c7cdf92 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Snippets/ModelReaderWriterSnippets.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel; +using System.ClientModel.Primitives; +using Microsoft.TypeSpec.Generator.Expressions; +using Microsoft.TypeSpec.Generator.Primitives; +using static Microsoft.TypeSpec.Generator.Snippets.Snippet; + +namespace Microsoft.TypeSpec.Generator.ClientModel.Snippets +{ + internal static class ModelReaderWriterSnippets + { + public static InvokeMethodExpression Read(ValueExpression data, CSharpType type) + { + return Static(typeof(ModelReaderWriter)).Invoke( + nameof(ModelReaderWriter.Read), + [data, ModelSerializationExtensionsSnippets.Wire, ModelReaderWriterContextSnippets.Default], + new CSharpType[] { type }); + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs index ba1f8c959eb..fb391a77a73 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs @@ -1133,5 +1133,66 @@ await MockHelpers.LoadMockGeneratorAsync( Assert.IsNotNull(collectionResultDefinition, "CollectionResultDefinition should be added even when paging methods are customized"); } + [TestCase("ListOfStrings", typeof(IReadOnlyList))] + [TestCase("DictionaryOfInts", typeof(IReadOnlyDictionary))] + [TestCase("Int32", typeof(int))] + [TestCase("Enum", typeof(int))] // Enum is tested with underlying type + public void AotCompatibleModelReaderWriterReadMethods(string testCaseName, Type returnType) + { + InputType? inputType = null; + bool isEnum = testCaseName == "Enum"; + + if (testCaseName == "ListOfStrings") + { + inputType = InputFactory.Array(InputPrimitiveType.String); + } + else if (testCaseName == "DictionaryOfInts") + { + inputType = InputFactory.Dictionary(InputPrimitiveType.Int32); + } + else if (testCaseName == "Int32") + { + inputType = InputPrimitiveType.Int32; + } + else if (isEnum) + { + inputType = InputFactory.Int32Enum("TestEnum", [("Value1", 1), ("Value2", 2)]); + } + + var inputOperation = InputFactory.Operation( + "GetData", + responses: [InputFactory.OperationResponse([200], inputType!)]); + + var inputServiceMethod = InputFactory.BasicServiceMethod("GetData", inputOperation); + var inputClient = InputFactory.Client("TestClient", methods: [inputServiceMethod]); + + MockHelpers.LoadMockGenerator(); + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient); + + var methodCollection = new ScmMethodProviderCollection(inputServiceMethod, client!); + Assert.IsNotNull(methodCollection); + + var convenienceMethod = methodCollection.FirstOrDefault(m + => m.Signature.Parameters.All(p => p.Name != "options") + && m.Signature.Name == $"{inputOperation.Name.ToIdentifierName()}"); + + Assert.IsNotNull(convenienceMethod); + + // Verify the body uses ModelReaderWriter.Read with context parameter + var bodyText = convenienceMethod!.BodyStatements!.ToDisplayString(); + + StringAssert.Contains("ModelReaderWriter.Read", bodyText, + "Method should use ModelReaderWriter.Read for AOT compatibility"); + StringAssert.Contains("SampleContext.Default", bodyText, + "Method should include ModelReaderWriterContext.Default parameter"); + StringAssert.Contains("ModelSerializationExtensions.WireOptions", bodyText, + "Method should include WireOptions parameter"); + + // Compare with expected output (normalize line endings for cross-platform compatibility) + var expected = Helpers.GetExpectedFromFile(testCaseName).Replace("\r\n", "\n"); + var actual = bodyText.Replace("\r\n", "\n"); + Assert.AreEqual(expected, actual); + } + } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(DictionaryOfInts).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(DictionaryOfInts).cs new file mode 100644 index 00000000000..feab0b10ccd --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(DictionaryOfInts).cs @@ -0,0 +1,2 @@ +global::System.ClientModel.ClientResult result = this.GetData(cancellationToken.ToRequestOptions()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read>(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(Enum).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(Enum).cs new file mode 100644 index 00000000000..6639001b13b --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(Enum).cs @@ -0,0 +1,2 @@ +global::System.ClientModel.ClientResult result = this.GetData(cancellationToken.ToRequestOptions()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default).ToTestEnum(), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(Int32).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(Int32).cs new file mode 100644 index 00000000000..e95c6c5c4e1 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(Int32).cs @@ -0,0 +1,2 @@ +global::System.ClientModel.ClientResult result = this.GetData(cancellationToken.ToRequestOptions()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(ListOfStrings).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(ListOfStrings).cs new file mode 100644 index 00000000000..8e0d77b127b --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/AotCompatibleModelReaderWriterReadMethods(ListOfStrings).cs @@ -0,0 +1,2 @@ +global::System.ClientModel.ClientResult result = this.GetData(cancellationToken.ToRequestOptions()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read>(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Boolean).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Boolean).cs index a418e708844..07904fb2083 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Boolean).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Boolean).cs @@ -1,2 +1,2 @@ global::System.ClientModel.ClientResult result = this.GetScalar(cancellationToken.ToRequestOptions()); -return global::System.ClientModel.ClientResult.FromValue(result.GetRawResponse().Content.ToObjectFromJson(), result.GetRawResponse()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(DateTimeOffset).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(DateTimeOffset).cs index dd967f8292f..876942e4c63 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(DateTimeOffset).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(DateTimeOffset).cs @@ -1,2 +1,2 @@ global::System.ClientModel.ClientResult result = this.GetScalar(cancellationToken.ToRequestOptions()); -return global::System.ClientModel.ClientResult.FromValue(result.GetRawResponse().Content.ToObjectFromJson(), result.GetRawResponse()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Double).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Double).cs index b6d44cd9c10..f8e487a6c13 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Double).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Double).cs @@ -1,2 +1,2 @@ global::System.ClientModel.ClientResult result = this.GetScalar(cancellationToken.ToRequestOptions()); -return global::System.ClientModel.ClientResult.FromValue(result.GetRawResponse().Content.ToObjectFromJson(), result.GetRawResponse()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32).cs index cb0644c9c72..75b473be99f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32).cs @@ -1,2 +1,2 @@ global::System.ClientModel.ClientResult result = this.GetScalar(cancellationToken.ToRequestOptions()); -return global::System.ClientModel.ClientResult.FromValue(result.GetRawResponse().Content.ToObjectFromJson(), result.GetRawResponse()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int64).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int64).cs index 558d73a47ea..58dade3b44a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int64).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int64).cs @@ -1,2 +1,2 @@ global::System.ClientModel.ClientResult result = this.GetScalar(cancellationToken.ToRequestOptions()); -return global::System.ClientModel.ClientResult.FromValue(result.GetRawResponse().Content.ToObjectFromJson(), result.GetRawResponse()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Single).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Single).cs index df2eb3be24c..7c98937ec7c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Single).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Single).cs @@ -1,2 +1,2 @@ global::System.ClientModel.ClientResult result = this.GetScalar(cancellationToken.ToRequestOptions()); -return global::System.ClientModel.ClientResult.FromValue(result.GetRawResponse().Content.ToObjectFromJson(), result.GetRawResponse()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(String).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(String).cs index 1cf02c42f1e..ad704dd2bcf 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(String).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(String).cs @@ -1,2 +1,2 @@ global::System.ClientModel.ClientResult result = this.GetScalar(cancellationToken.ToRequestOptions()); -return global::System.ClientModel.ClientResult.FromValue(result.GetRawResponse().Content.ToObjectFromJson(), result.GetRawResponse()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse()); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(TimeSpan).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(TimeSpan).cs index 9012ebf182c..30b622b37f4 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(TimeSpan).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(TimeSpan).cs @@ -1,2 +1,2 @@ global::System.ClientModel.ClientResult result = this.GetScalar(cancellationToken.ToRequestOptions()); -return global::System.ClientModel.ClientResult.FromValue(result.GetRawResponse().Content.ToObjectFromJson(), result.GetRawResponse()); +return global::System.ClientModel.ClientResult.FromValue(global::System.ClientModel.Primitives.ModelReaderWriter.Read(result.GetRawResponse().Content, global::Sample.ModelSerializationExtensions.WireOptions, global::Sample.SampleContext.Default), result.GetRawResponse());