diff --git a/App/Startup.cs b/App/Startup.cs index 4b4486d4..544b307a 100644 --- a/App/Startup.cs +++ b/App/Startup.cs @@ -49,8 +49,7 @@ public void ConfigureServices(IServiceCollection services) services .AddWebApiClient() .UseJsonFirstApiActionDescriptor() - .UseSourceGeneratorHttpApiActivator() - .AddDynamicDependencyApp(); + .UseSourceGeneratorHttpApiActivator(); // עuserApi services.AddHttpApi(typeof(IUserApi), o => diff --git a/AppAot/AppAot.csproj b/AppAot/AppAot.csproj new file mode 100644 index 00000000..b949817e --- /dev/null +++ b/AppAot/AppAot.csproj @@ -0,0 +1,24 @@ + + + + Exe + net8.0 + false + enable + true + true + true + + + + + + + + + + + + + + diff --git a/AppAot/AppData.cs b/AppAot/AppData.cs new file mode 100644 index 00000000..69b21421 --- /dev/null +++ b/AppAot/AppData.cs @@ -0,0 +1,7 @@ +namespace AppAot +{ + public class AppData + { + public string? WebpackCompilationHash { get; set; } + } +} diff --git a/AppAot/AppHostedService.cs b/AppAot/AppHostedService.cs new file mode 100644 index 00000000..eb43c58c --- /dev/null +++ b/AppAot/AppHostedService.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace AppAot +{ + class AppHostedService : BackgroundService + { + private readonly IServiceScopeFactory serviceScopeFactory; + private readonly ILogger logger; + + public AppHostedService( + IServiceScopeFactory serviceScopeFactory, + ILogger logger) + { + this.serviceScopeFactory = serviceScopeFactory; + this.logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + using var scope = this.serviceScopeFactory.CreateScope(); + var api = scope.ServiceProvider.GetRequiredService(); + var appData = await api.GetAppDataAsync(); + appData = await api.GetAppData2Async(); + this.logger.LogInformation($"WebpackCompilationHash: {appData.WebpackCompilationHash}"); + } + } +} diff --git a/AppAot/AppJsonSerializerContext.cs b/AppAot/AppJsonSerializerContext.cs new file mode 100644 index 00000000..f64fe137 --- /dev/null +++ b/AppAot/AppJsonSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace AppAot +{ + [JsonSerializable(typeof(AppData[]))] + partial class AppJsonSerializerContext : JsonSerializerContext + { + } +} diff --git a/AppAot/ICloudflareApi.cs b/AppAot/ICloudflareApi.cs new file mode 100644 index 00000000..6bdd6b7e --- /dev/null +++ b/AppAot/ICloudflareApi.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using WebApiClientCore; +using WebApiClientCore.Attributes; + +namespace AppAot +{ + [HttpHost("https://www.cloudflare-cn.com")] + [LoggingFilter] + public interface ICloudflareApi + { + [HttpGet("/page-data/app-data.json")] + Task GetAppDataAsync(); + + [HttpGet("/page-data/app-data.json")] + ITask GetAppData2Async(); + } +} diff --git a/AppAot/Program.cs b/AppAot/Program.cs new file mode 100644 index 00000000..cb671cb9 --- /dev/null +++ b/AppAot/Program.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace AppAot +{ + class Program + { + static void Main(string[] args) + { + Host.CreateDefaultBuilder(args) + .ConfigureServices(services => + { + services + .AddWebApiClient() + .UseSourceGeneratorHttpApiActivator() // SG 激活器 + .ConfigureHttpApi(options => // json SG生成器配置 + { + var jsonContext = AppJsonSerializerContext.Default; + options.JsonSerializeOptions.TypeInfoResolverChain.Insert(0, jsonContext); + options.JsonDeserializeOptions.TypeInfoResolverChain.Insert(0, jsonContext); + options.KeyValueSerializeOptions.GetJsonSerializerOptions().TypeInfoResolverChain.Insert(0, jsonContext); + }); + + services.AddHttpApi(); + services.AddHostedService(); + }) + .Build() + .Run(); + } + } +} diff --git a/Directory.Build.props b/Directory.Build.props index 26dc5f77..471696cf 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 2.0.5 - Copyright © laojiu 2017-2023 + 2.0.6 + Copyright © laojiu 2017-2024 IDE0057 diff --git a/WebApiClientCore.Abstractions/IHttpApiActivator.cs b/WebApiClientCore.Abstractions/IHttpApiActivator.cs index f2a07542..46137951 100644 --- a/WebApiClientCore.Abstractions/IHttpApiActivator.cs +++ b/WebApiClientCore.Abstractions/IHttpApiActivator.cs @@ -4,7 +4,11 @@ /// 定义THttpApi的实例创建器的接口 /// /// - public interface IHttpApiActivator + public interface IHttpApiActivator< +#if NET5_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + THttpApi> { /// /// 创建THttpApi的代理实例 diff --git a/WebApiClientCore.Abstractions/WebApiClientCore.Abstractions.csproj b/WebApiClientCore.Abstractions/WebApiClientCore.Abstractions.csproj index fab7a852..579f5112 100644 --- a/WebApiClientCore.Abstractions/WebApiClientCore.Abstractions.csproj +++ b/WebApiClientCore.Abstractions/WebApiClientCore.Abstractions.csproj @@ -2,7 +2,7 @@ enable - netstandard2.1 + netstandard2.1;net5.0 WebApiClientCore WebApiClientCore.Abstractions diff --git a/WebApiClientCore.Analyzers.SourceGenerator/DynamicDependencyBuilder.cs b/WebApiClientCore.Analyzers.SourceGenerator/DynamicDependencyBuilder.cs index 7504abcc..c547c835 100644 --- a/WebApiClientCore.Analyzers.SourceGenerator/DynamicDependencyBuilder.cs +++ b/WebApiClientCore.Analyzers.SourceGenerator/DynamicDependencyBuilder.cs @@ -11,8 +11,8 @@ sealed class DynamicDependencyBuilder private readonly Compilation compilation; private readonly IEnumerable codeBuilders; - public string FileName => "WebApiClientBuilderExtensions.g.cs"; - public string ClassName => "WebApiClientBuilderExtensions_G"; + public string FileName => "DynamicDependencyInitializer.g.cs"; + public string ClassName => "DynamicDependencyInitializer_G"; public DynamicDependencyBuilder(Compilation compilation, IEnumerable codeBuilders) { @@ -35,10 +35,11 @@ public override string ToString() var builder = new StringBuilder(); builder.AppendLine("#if NET5_0_OR_GREATER"); builder.AppendLine("using System.Diagnostics.CodeAnalysis;"); - builder.AppendLine("namespace Microsoft.Extensions.DependencyInjection"); + builder.AppendLine("using System.Runtime.CompilerServices;"); + builder.AppendLine($"namespace WebApiClientCore.Implementations"); builder.AppendLine("{"); - builder.AppendLine(" /// IWebApiClientBuilder扩展"); - builder.AppendLine($" public static partial class {this.ClassName}"); + builder.AppendLine(" /// 动态依赖初始化器"); + builder.AppendLine($" static partial class {this.ClassName}"); builder.AppendLine(" {"); builder.AppendLine($""" @@ -46,36 +47,29 @@ public override string ToString() /// 注册程序集{compilation.AssemblyName}的所有动态依赖 /// 避免程序集在裁剪时裁剪掉由SourceGenerator生成的代理类 /// - /// - /// """); - var assemblyName = GetAssemblyName(compilation); + builder.AppendLine(" [ModuleInitializer]"); foreach (var codeBuilder in this.codeBuilders) { builder.AppendLine($" [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof({codeBuilder.Namespace}.{codeBuilder.ClassName}))]"); } - builder.AppendLine($" public static IWebApiClientBuilder AddDynamicDependency{assemblyName}(this IWebApiClientBuilder builder)"); + var resultTypes = this.codeBuilders.SelectMany(item => item.GetResultTypes()).Distinct(SymbolEqualityComparer.Default); + foreach(var resultType in resultTypes) + { + builder.AppendLine($" [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(DefaultApiActionInvoker<{resultType}>))]"); + } + + builder.AppendLine(" public static void AddDynamicDependency()"); builder.AppendLine(" {"); - builder.AppendLine(" return builder;"); builder.AppendLine(" }"); - + builder.AppendLine(" }"); builder.AppendLine("}"); builder.AppendLine("#endif"); return builder.ToString(); } - private static string GetAssemblyName(Compilation compilation) - { - var assemblyName = compilation.AssemblyName ?? string.Empty; - return new string(assemblyName.Where(IsAllowChar).ToArray()); - - static bool IsAllowChar(char c) - { - return ('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); - } - } } } diff --git a/WebApiClientCore.Analyzers.SourceGenerator/HttpApiCodeBuilder.cs b/WebApiClientCore.Analyzers.SourceGenerator/HttpApiCodeBuilder.cs index 1e176da3..0f281d9f 100644 --- a/WebApiClientCore.Analyzers.SourceGenerator/HttpApiCodeBuilder.cs +++ b/WebApiClientCore.Analyzers.SourceGenerator/HttpApiCodeBuilder.cs @@ -128,6 +128,22 @@ public override string ToString() } + public IEnumerable GetResultTypes() + { + var methods = HttpApiMethodFinder.FindApiMethods(this.httpApi); + foreach (var method in methods) + { + if (method.ReturnType is INamedTypeSymbol typeSymbol) + { + var resultType = typeSymbol.TypeArguments.FirstOrDefault(); + if (resultType != null) + { + yield return resultType; + } + } + } + } + /// /// 构建方法 /// diff --git a/WebApiClientCore.Analyzers.SourceGenerator/HttpApiSourceGenerator.cs b/WebApiClientCore.Analyzers.SourceGenerator/HttpApiSourceGenerator.cs index df89ed50..b72a6cb9 100644 --- a/WebApiClientCore.Analyzers.SourceGenerator/HttpApiSourceGenerator.cs +++ b/WebApiClientCore.Analyzers.SourceGenerator/HttpApiSourceGenerator.cs @@ -29,15 +29,19 @@ public void Execute(GeneratorExecutionContext context) var builders = receiver .GetHttpApiTypes(context.Compilation) .Select(i => new HttpApiCodeBuilder(i)) - .Distinct(); + .Distinct() + .ToArray(); foreach (var builder in builders) { context.AddSource(builder.HttpApiTypeName, builder.ToSourceText()); } - var dependencyBuilder = new DynamicDependencyBuilder(context.Compilation, builders); - context.AddSource(dependencyBuilder.FileName, dependencyBuilder.ToSourceText()); + if (builders.Length > 0) + { + var dependencyBuilder = new DynamicDependencyBuilder(context.Compilation, builders); + context.AddSource(dependencyBuilder.FileName, dependencyBuilder.ToSourceText()); + } } } } diff --git a/WebApiClientCore.Extensions.SourceGenerator/Implementations/SourceGeneratorHttpApiActivator.cs b/WebApiClientCore.Extensions.SourceGenerator/Implementations/SourceGeneratorHttpApiActivator.cs index f6447ac1..3cf26bbe 100644 --- a/WebApiClientCore.Extensions.SourceGenerator/Implementations/SourceGeneratorHttpApiActivator.cs +++ b/WebApiClientCore.Extensions.SourceGenerator/Implementations/SourceGeneratorHttpApiActivator.cs @@ -10,7 +10,11 @@ namespace WebApiClientCore.Implementations /// 通过查找类型代理类型创建实例 /// /// - public class SourceGeneratorHttpApiActivator : IHttpApiActivator + public class SourceGeneratorHttpApiActivator< +#if NET5_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + THttpApi> : IHttpApiActivator { private readonly ApiActionInvoker[] actionInvokers; private readonly Func activator; diff --git a/WebApiClientCore.Extensions.SourceGenerator/WebApiClientCore.Extensions.SourceGenerator.csproj b/WebApiClientCore.Extensions.SourceGenerator/WebApiClientCore.Extensions.SourceGenerator.csproj index 1d23058b..fb717eb7 100644 --- a/WebApiClientCore.Extensions.SourceGenerator/WebApiClientCore.Extensions.SourceGenerator.csproj +++ b/WebApiClientCore.Extensions.SourceGenerator/WebApiClientCore.Extensions.SourceGenerator.csproj @@ -1,9 +1,8 @@ - - 2.0.5.1 + enable - netstandard2.1 + netstandard2.1;net5.0 bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml true diff --git a/WebApiClientCore.OpenApi.SourceGenerator/WebApiClientCore.OpenApi.SourceGenerator.csproj b/WebApiClientCore.OpenApi.SourceGenerator/WebApiClientCore.OpenApi.SourceGenerator.csproj index b780f630..e716c31f 100644 --- a/WebApiClientCore.OpenApi.SourceGenerator/WebApiClientCore.OpenApi.SourceGenerator.csproj +++ b/WebApiClientCore.OpenApi.SourceGenerator/WebApiClientCore.OpenApi.SourceGenerator.csproj @@ -1,7 +1,6 @@  - - 2.0.4.1 + Exe netcoreapp3.1;net5;net6;net7 diff --git a/WebApiClientCore.sln b/WebApiClientCore.sln index ddd1a0c1..49d05c4b 100644 --- a/WebApiClientCore.sln +++ b/WebApiClientCore.sln @@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiClientCore.Extensions EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiClientCore.Analyzers.SourceGenerator", "WebApiClientCore.Analyzers.SourceGenerator\WebApiClientCore.Analyzers.SourceGenerator.csproj", "{DFE35D63-0888-4EE2-A9F1-AC45756F5909}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppAot", "AppAot\AppAot.csproj", "{F77DA016-1F63-46BF-A5A0-AD4662D528B9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -81,6 +83,10 @@ Global {DFE35D63-0888-4EE2-A9F1-AC45756F5909}.Debug|Any CPU.Build.0 = Debug|Any CPU {DFE35D63-0888-4EE2-A9F1-AC45756F5909}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFE35D63-0888-4EE2-A9F1-AC45756F5909}.Release|Any CPU.Build.0 = Release|Any CPU + {F77DA016-1F63-46BF-A5A0-AD4662D528B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F77DA016-1F63-46BF-A5A0-AD4662D528B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F77DA016-1F63-46BF-A5A0-AD4662D528B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F77DA016-1F63-46BF-A5A0-AD4662D528B9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WebApiClientCore/Implementations/DefaultApiActionInvoker.cs b/WebApiClientCore/Implementations/DefaultApiActionInvoker.cs index 6f4faaa0..69e96633 100644 --- a/WebApiClientCore/Implementations/DefaultApiActionInvoker.cs +++ b/WebApiClientCore/Implementations/DefaultApiActionInvoker.cs @@ -65,7 +65,7 @@ public virtual async Task InvokeAsync(HttpClientContext context, object var httpContext = new HttpContext(context, requestMessage); var requestContext = new ApiRequestContext(httpContext, this.ActionDescriptor, arguments, new DefaultDataCollection()); - return await this.InvokeAsync(requestContext).ConfigureAwait(false); + return await InvokeAsync(requestContext).ConfigureAwait(false); } catch (HttpRequestException) { @@ -83,7 +83,7 @@ public virtual async Task InvokeAsync(HttpClientContext context, object /// /// /// - private async Task InvokeAsync(ApiRequestContext request) + private static async Task InvokeAsync(ApiRequestContext request) { #nullable disable var response = await ApiRequestExecuter.ExecuteAsync(request).ConfigureAwait(false); @@ -136,7 +136,7 @@ public ITaskReturnActionInvoker(DefaultApiActionInvoker actionInvoker) public override object Invoke(HttpClientContext context, object?[] arguments) { return new ActionTask(this.actionInvoker, context, arguments); - } + } } } } diff --git a/WebApiClientCore/Implementations/DefaultHttpApiActivator.cs b/WebApiClientCore/Implementations/DefaultHttpApiActivator.cs index ce6a9adb..51b3ede1 100644 --- a/WebApiClientCore/Implementations/DefaultHttpApiActivator.cs +++ b/WebApiClientCore/Implementations/DefaultHttpApiActivator.cs @@ -11,7 +11,11 @@ namespace WebApiClientCore.Implementations /// 运行时使用Emit动态创建THttpApi的代理类和代理类实例 /// /// - public class DefaultHttpApiActivator : IHttpApiActivator + public class DefaultHttpApiActivator< +#if NET5_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + THttpApi> : IHttpApiActivator { private readonly ApiActionInvoker[] actionInvokers; private readonly Func activator; diff --git a/WebApiClientCore/Resx.Designer.cs b/WebApiClientCore/Resx.Designer.cs index 02acfbf2..81971bf8 100644 --- a/WebApiClientCore/Resx.Designer.cs +++ b/WebApiClientCore/Resx.Designer.cs @@ -19,7 +19,7 @@ namespace WebApiClientCore { // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resx { diff --git a/WebApiClientCore/WebApiClientCore.csproj b/WebApiClientCore/WebApiClientCore.csproj index 2bbabbf8..d2585c3d 100644 --- a/WebApiClientCore/WebApiClientCore.csproj +++ b/WebApiClientCore/WebApiClientCore.csproj @@ -2,7 +2,7 @@ enable - netstandard2.1 + netstandard2.1;net5.0 bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml .NetCore声明式的Http客户端库