diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index 081342b913..cb63a9e6c8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -1,5 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.Extensions.DependencyInjection; @@ -14,6 +18,7 @@ using Volo.Abp.AspNetCore.Mvc.Response; using Volo.Abp.AspNetCore.Mvc.Uow; using Volo.Abp.AspNetCore.Mvc.Validation; using Volo.Abp.Content; +using Volo.Abp.Json.SystemTextJson.JsonConverters; namespace Volo.Abp.AspNetCore.Mvc; @@ -32,6 +37,17 @@ internal static class AbpMvcOptionsExtensions private static void AddFormatters(MvcOptions options) { options.OutputFormatters.Insert(0, new RemoteStreamContentOutputFormatter()); + var systemTextJsonOutputFormatter = options.OutputFormatters + .Where(f => f is SystemTextJsonOutputFormatter) + .Cast().FirstOrDefault(); + + if (systemTextJsonOutputFormatter != null) + { + options.OutputFormatters.Remove(systemTextJsonOutputFormatter); + var jsonOptions = new JsonSerializerOptions(systemTextJsonOutputFormatter.SerializerOptions); + jsonOptions.Converters.RemoveAll(x => x is ObjectToInferredTypesConverter); + options.OutputFormatters.Add(new SystemTextJsonOutputFormatter(jsonOptions)); + } } private static void AddConventions(MvcOptions options, IServiceCollection services) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs index e0dcc14fb1..64947e637f 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs @@ -19,7 +19,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase" + $"{Environment.NewLine}" + $"{Environment.NewLine}// ReSharper disable once CheckNamespace" + @@ -44,7 +44,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase;" + $"{Environment.NewLine}" + @@ -53,7 +53,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase" + $"{Environment.NewLine}" + $"{Environment.NewLine}// ReSharper disable once CheckNamespace" + @@ -65,7 +65,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase" + $"{Environment.NewLine}" + $"{Environment.NewLine}// ReSharper disable once CheckNamespace" + @@ -77,7 +77,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase ClassUsingNamespaceList = new() + private static readonly List ClassUsingNamespaceList = new() { "using System;", "using System.Collections.Generic;", @@ -90,7 +90,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase InterfaceUsingNamespaceList = new() + private static readonly List InterfaceUsingNamespaceList = new() { "using System;", "using System.Collections.Generic;", @@ -100,7 +100,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase DtoUsingNamespaceList = new() + private static readonly List DtoUsingNamespaceList = new() { "using System;", "using System.Collections.Generic;", @@ -116,7 +116,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase usingNamespaceList) { - var returnSign = returnTypeName == "void" ? "Task" : $"Task<{returnTypeName}>"; + var isAsyncEnumerable = returnTypeName.StartsWith("IAsyncEnumerable<"); + var asyncEnumerableTypeName = isAsyncEnumerable + ? returnTypeName.Substring("IAsyncEnumerable<".Length, returnTypeName.Length - "IAsyncEnumerable<".Length - 1) + : null; + + var returnSign = isAsyncEnumerable ? returnTypeName : returnTypeName == "void" ? "Task" : $"Task<{returnTypeName}>"; - methodBuilder.AppendLine($"public virtual async {returnSign} {action.Name}()"); + methodBuilder.AppendLine(isAsyncEnumerable + ? $"public virtual {returnSign} {action.Name}()" + : $"public virtual async {returnSign} {action.Name}()"); foreach (var parameter in action.ParametersOnMethod) { @@ -325,9 +332,11 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase(nameof({action.Name}), {args});"); + methodBuilder.AppendLine(isAsyncEnumerable + ? $" return RequestAsyncEnumerable<{asyncEnumerableTypeName}>(nameof({action.Name}), {args});" + : returnTypeName == "void" + ? $" await RequestAsync(nameof({action.Name}), {args});" + : $" return await RequestAsync<{returnTypeName}>(nameof({action.Name}), {args});"); foreach (var parameter in action.ParametersOnMethod) { @@ -543,7 +552,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase : ITransientDependency protected ClientProxyUrlBuilder ClientProxyUrlBuilder => LazyServiceProvider.LazyGetRequiredService(); protected ICurrentApiVersionInfo CurrentApiVersionInfo => LazyServiceProvider.LazyGetRequiredService(); protected ILocalEventBus LocalEventBus => LazyServiceProvider.LazyGetRequiredService(); + protected IOptions? SystemTextJsonSerializerOptions => LazyServiceProvider.LazyGetService>(); protected virtual async Task RequestAsync(string methodName, ClientProxyRequestTypeValue? arguments = null) { @@ -55,6 +58,21 @@ public class ClientProxyBase : ITransientDependency return await RequestAsync(BuildHttpProxyClientProxyContext(methodName, arguments)); } + protected virtual async IAsyncEnumerable RequestAsyncEnumerable(string methodName, ClientProxyRequestTypeValue? arguments = null) + { + var requestContext = BuildHttpProxyClientProxyContext(methodName, arguments); + var responseContent = await RequestAsync(requestContext); + var options = SystemTextJsonSerializerOptions?.Value.JsonSerializerOptions; + var stream = await responseContent.ReadAsStreamAsync(); + var items = options != null + ? System.Text.Json.JsonSerializer.DeserializeAsyncEnumerable(stream, options) + : System.Text.Json.JsonSerializer.DeserializeAsyncEnumerable(stream); + await foreach (var item in items) + { + yield return item!; + } + } + protected virtual ClientProxyRequestContext BuildHttpProxyClientProxyContext(string methodName, ClientProxyRequestTypeValue? arguments = null) { if (arguments == null)