diff --git a/Util.sln b/Util.sln index 6b5150e65..001cba2de 100644 --- a/Util.sln +++ b/Util.sln @@ -21,7 +21,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Core.Tests", "test\Uti EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "13-Ui", "13-Ui", "{354B3720-D050-487D-A46A-22BCFEE56D43}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "14-Ui", "14-Ui", "{354B3720-D050-487D-A46A-22BCFEE56D43}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Ui", "src\Util.Ui\01-Util.Ui.csproj", "{5F7ACBE4-A1D8-4680-AA86-B87F8494BAC2}" EndProject @@ -31,7 +31,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Ui.NgZorro.Tests", "te EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-Core", "01-Core", "{9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "14-Ui", "14-Ui", "{DE39BB20-AE82-47C3-9141-8CFD4C673217}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "15-Ui", "15-Ui", "{DE39BB20-AE82-47C3-9141-8CFD4C673217}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Ui.Tests", "test\Util.Ui.Tests\Util.Ui.Tests.csproj", "{766CAB38-F064-419F-837B-D6DA8B61AF66}" EndProject @@ -257,13 +257,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Images.Avatar", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Images.Avatar.Tests.Integration", "test\Util.Images.Avatar.Tests.Integration\Util.Images.Avatar.Tests.Integration.csproj", "{75A31AAB-B7E6-4F13-9B7A-B74216B2EDDE}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "14-Generators", "14-Generators", "{411247A4-4665-4BE6-8E5C-08F6D0BA2213}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "15-Generators", "15-Generators", "{411247A4-4665-4BE6-8E5C-08F6D0BA2213}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Generators", "src\Util.Generators\01-Util.Generators.csproj", "{36990D32-6F41-44A6-BB88-A3F15E12C35B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Generators.Razor", "src\Util.Generators.Razor\02-Util.Generators.Razor.csproj", "{21831A4F-B3C9-40E7-855B-75F9D679FF55}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "15-Generators", "15-Generators", "{F7D4E7CB-468D-4570-8D82-D065F08BE40D}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "16-Generators", "16-Generators", "{F7D4E7CB-468D-4570-8D82-D065F08BE40D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Generators.Tests", "test\Util.Generators.Tests\Util.Generators.Tests.csproj", "{5996F536-14B9-4F95-A18B-716E5F7EE478}" EndProject @@ -285,8 +285,26 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Generators.Razor.Tests EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Events.Tests.Integration", "test\Util.Events.Tests.Integration\Util.Events.Tests.Integration.csproj", "{FB11256C-06C0-4B24-863A-C87D78F8EE2A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "14-Microservices", "14-Microservices", "{547FD391-A56B-47A6-B20C-5FC04FA15C56}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Events.Abstractions", "src\Util.Events.Abstractions\01-Util.Events.Abstractions.csproj", "{6233BF36-9636-43BD-9462-2D31A05EE4ED}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "13-Microservices", "13-Microservices", "{1DEC52C8-6692-4DA7-AE34-B76D728F895A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Microservices", "src\Util.Microservices\01-Util.Microservices.csproj", "{F8417623-6936-43C4-A967-9D6BD002AD16}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Microservices.Dapr", "src\Util.Microservices.Dapr\03-Util.Microservices.Dapr.csproj", "{CAA32331-4A8E-4C16-BC7D-7CA646B393F4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Microservices.Dapr.Tests.Integration", "test\Util.Microservices.Dapr.Tests.Integration\Util.Microservices.Dapr.Tests.Integration.csproj", "{08B1A026-4F8C-4336-B5B8-3189023DFF2B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Microservices.Polly", "src\Util.Microservices.Polly\02-Util.Microservices.Polly.csproj", "{0BCB6FF1-E374-4AED-9888-83C7CFED0E98}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Microservices.Polly.Tests.Integration", "test\Util.Microservices.Polly.Tests.Integration\Util.Microservices.Polly.Tests.Integration.csproj", "{DF1E55A5-7EA8-41B2-BEBA-72544D256069}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "04-Util.Microservices.HealthChecks", "src\Util.Microservices.HealthChecks\04-Util.Microservices.HealthChecks.csproj", "{9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Microservices.Dapr.WebApiSample", "test\Util.Microservices.Dapr.WebApiSample\Util.Microservices.Dapr.WebApiSample.csproj", "{AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -701,6 +719,34 @@ Global {6233BF36-9636-43BD-9462-2D31A05EE4ED}.Debug|Any CPU.Build.0 = Debug|Any CPU {6233BF36-9636-43BD-9462-2D31A05EE4ED}.Release|Any CPU.ActiveCfg = Release|Any CPU {6233BF36-9636-43BD-9462-2D31A05EE4ED}.Release|Any CPU.Build.0 = Release|Any CPU + {F8417623-6936-43C4-A967-9D6BD002AD16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8417623-6936-43C4-A967-9D6BD002AD16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8417623-6936-43C4-A967-9D6BD002AD16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8417623-6936-43C4-A967-9D6BD002AD16}.Release|Any CPU.Build.0 = Release|Any CPU + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4}.Release|Any CPU.Build.0 = Release|Any CPU + {08B1A026-4F8C-4336-B5B8-3189023DFF2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08B1A026-4F8C-4336-B5B8-3189023DFF2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08B1A026-4F8C-4336-B5B8-3189023DFF2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08B1A026-4F8C-4336-B5B8-3189023DFF2B}.Release|Any CPU.Build.0 = Release|Any CPU + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98}.Release|Any CPU.Build.0 = Release|Any CPU + {DF1E55A5-7EA8-41B2-BEBA-72544D256069}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF1E55A5-7EA8-41B2-BEBA-72544D256069}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF1E55A5-7EA8-41B2-BEBA-72544D256069}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF1E55A5-7EA8-41B2-BEBA-72544D256069}.Release|Any CPU.Build.0 = Release|Any CPU + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}.Release|Any CPU.Build.0 = Release|Any CPU + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -842,7 +888,16 @@ Global {4FFD1073-9358-418D-BB4D-DB63FE0A11DF} = {445850B7-1A9D-4E9D-8AC1-765F3792DC19} {5BFF8BA0-EA27-4493-88A5-5BC57286D536} = {F7D4E7CB-468D-4570-8D82-D065F08BE40D} {FB11256C-06C0-4B24-863A-C87D78F8EE2A} = {3ABD7B4B-37F9-4D49-92D9-384190177286} + {547FD391-A56B-47A6-B20C-5FC04FA15C56} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} {6233BF36-9636-43BD-9462-2D31A05EE4ED} = {59F82491-51E3-4A12-B427-9815AEA23D10} + {1DEC52C8-6692-4DA7-AE34-B76D728F895A} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {F8417623-6936-43C4-A967-9D6BD002AD16} = {1DEC52C8-6692-4DA7-AE34-B76D728F895A} + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4} = {1DEC52C8-6692-4DA7-AE34-B76D728F895A} + {08B1A026-4F8C-4336-B5B8-3189023DFF2B} = {547FD391-A56B-47A6-B20C-5FC04FA15C56} + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98} = {1DEC52C8-6692-4DA7-AE34-B76D728F895A} + {DF1E55A5-7EA8-41B2-BEBA-72544D256069} = {547FD391-A56B-47A6-B20C-5FC04FA15C56} + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F} = {1DEC52C8-6692-4DA7-AE34-B76D728F895A} + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0} = {547FD391-A56B-47A6-B20C-5FC04FA15C56} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94347832-A36D-4C42-9C4D-B848BD4F5DA9} diff --git a/build/BuildScript.cs b/build/BuildScript.cs index 04e7f1c9c..d6aa96889 100644 --- a/build/BuildScript.cs +++ b/build/BuildScript.cs @@ -199,6 +199,7 @@ private void PublishNuGetPackage( ITaskContext context, params ITarget[] dependT .DoNotFailOnError( ex => { Console.WriteLine( $"Failed to publish {package}.exception: {ex.Message}" ); } ) .ServerUrl( NugetUrl ) .ApiKey( NugetApiKey ) + .SkipDuplicate() .Execute( context ); } } ); diff --git a/build/version.props b/build/version.props index 4b5a05e30..1932c79c7 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 7 1 - 2 + 11 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/src/Util.Aop.AspectCore/Usings.cs b/src/Util.Aop.AspectCore/Usings.cs index b63a1b77e..059e6cc71 100644 --- a/src/Util.Aop.AspectCore/Usings.cs +++ b/src/Util.Aop.AspectCore/Usings.cs @@ -1,30 +1,6 @@ global using System; global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using AspectCore.Configuration; global using AspectCore.DynamicProxy; global using AspectCore.DynamicProxy.Parameters; diff --git a/src/Util.Application.EntityFrameworkCore/Usings.cs b/src/Util.Application.EntityFrameworkCore/Usings.cs index c27b20cbe..7bf07ab32 100644 --- a/src/Util.Application.EntityFrameworkCore/Usings.cs +++ b/src/Util.Application.EntityFrameworkCore/Usings.cs @@ -2,29 +2,3 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; -global using Microsoft.AspNetCore.Mvc; -global using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; \ No newline at end of file diff --git a/src/Util.Application.WebApi/Controllers/WebApiControllerBase.cs b/src/Util.Application.WebApi/Controllers/WebApiControllerBase.cs index 3e30ced05..e2cd178b3 100644 --- a/src/Util.Application.WebApi/Controllers/WebApiControllerBase.cs +++ b/src/Util.Application.WebApi/Controllers/WebApiControllerBase.cs @@ -1,10 +1,11 @@ using Util.Applications.Filters; +using Util.AspNetCore; using Util.Helpers; using Util.Logging; using Util.Properties; using Util.Sessions; -namespace Util.Applications.Controllers; +namespace Util.Applications.Controllers; /// /// WebApi控制器基类 @@ -47,10 +48,20 @@ protected virtual IActionResult Success( dynamic data = null, string message = n /// 获取结果 /// private IActionResult GetResult( string code, string message, dynamic data, int? httpStatusCode ) { + var options = GetJsonSerializerOptions(); var resultFactory = HttpContext.RequestServices.GetService(); if ( resultFactory == null ) - return new Result( code, message, data, httpStatusCode ); - return resultFactory.CreateResult( code, message, data, httpStatusCode ); + return new Result( code, message, data, httpStatusCode, options ); + return resultFactory.CreateResult( code, message, data, httpStatusCode, options ); + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetJsonSerializerOptions() { + var factory = HttpContext.RequestServices.GetService(); + factory.CheckNull( nameof( factory ) ); + return factory.CreateOptions(); } /// diff --git a/src/Util.Application.WebApi/Filters/ExceptionHandlerAttribute.cs b/src/Util.Application.WebApi/Filters/ExceptionHandlerAttribute.cs index 4c6b8bd29..f66f4646b 100644 --- a/src/Util.Application.WebApi/Filters/ExceptionHandlerAttribute.cs +++ b/src/Util.Application.WebApi/Filters/ExceptionHandlerAttribute.cs @@ -1,5 +1,6 @@ using Util.Helpers; using Util.Exceptions; +using Util.AspNetCore; namespace Util.Applications.Filters; @@ -39,14 +40,23 @@ protected virtual string GetLocalizedMessages( ExceptionContext context, string return stringLocalizer[message]; } - /// /// 获取结果 /// protected virtual IActionResult GetResult( ExceptionContext context, string code, string message, int? httpStatusCode ) { + var options = GetJsonSerializerOptions( context ); var resultFactory = context.HttpContext.RequestServices.GetService(); if ( resultFactory == null ) - return new Result( code, message, null, httpStatusCode ); - return resultFactory.CreateResult( code, message, null, httpStatusCode ); + return new Result( code, message, null, httpStatusCode, options ); + return resultFactory.CreateResult( code, message, null, httpStatusCode, options ); + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetJsonSerializerOptions( ExceptionContext context ) { + var factory = context.HttpContext.RequestServices.GetService(); + factory.CheckNull( nameof( factory ) ); + return factory.CreateOptions(); } } \ No newline at end of file diff --git a/src/Util.Application.WebApi/Filters/LockAttribute.cs b/src/Util.Application.WebApi/Filters/LockAttribute.cs index 800c53fad..11bd62a68 100644 --- a/src/Util.Application.WebApi/Filters/LockAttribute.cs +++ b/src/Util.Application.WebApi/Filters/LockAttribute.cs @@ -1,4 +1,5 @@ using Util.Applications.Locks; +using Util.AspNetCore; using Util.Helpers; using Util.Properties; using Util.Sessions; @@ -89,10 +90,20 @@ protected string GetUserId( ActionExecutingContext context ) { /// 获取结果 /// private IActionResult GetResult( ActionExecutingContext context,string code, string message ) { + var options = GetJsonSerializerOptions( context ); var resultFactory = context.HttpContext.RequestServices.GetService(); if ( resultFactory == null ) - return new Result( code, message ); - return resultFactory.CreateResult( code, message, null, null ); + return new Result( code, message,options: options ); + return resultFactory.CreateResult( code, message, null, null, options ); + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetJsonSerializerOptions( ActionExecutingContext context ) { + var factory = context.HttpContext.RequestServices.GetService(); + factory.CheckNull( nameof( factory ) ); + return factory.CreateOptions(); } /// diff --git a/src/Util.Application.WebApi/IResultFactory.cs b/src/Util.Application.WebApi/IResultFactory.cs index 01db4318f..072e5ecab 100644 --- a/src/Util.Application.WebApi/IResultFactory.cs +++ b/src/Util.Application.WebApi/IResultFactory.cs @@ -13,5 +13,6 @@ public interface IResultFactory : ISingletonDependency { /// 消息 /// 数据 /// Http状态码 - IActionResult CreateResult( string code, string message, dynamic data, int? httpStatusCode ); + /// Json序列化配置 + IActionResult CreateResult( string code, string message, dynamic data, int? httpStatusCode, JsonSerializerOptions options ); } \ No newline at end of file diff --git a/src/Util.Application.WebApi/Infrastructure/WebApiServiceRegistrar.cs b/src/Util.Application.WebApi/Infrastructure/WebApiServiceRegistrar.cs index 078f2db0f..ed1438868 100644 --- a/src/Util.Application.WebApi/Infrastructure/WebApiServiceRegistrar.cs +++ b/src/Util.Application.WebApi/Infrastructure/WebApiServiceRegistrar.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http; using Util.Applications.Logging; +using Util.AspNetCore; using Util.Infrastructure; using Util.Logging; @@ -89,9 +90,19 @@ protected virtual string GetLocalizedMessages( HttpContext context, string messa /// 获取结果 /// protected virtual IActionResult GetResult( HttpContext context, string code, string message, int? httpStatusCode ) { + var options = GetJsonSerializerOptions( context ); var resultFactory = context.RequestServices.GetService(); if ( resultFactory == null ) - return new Result( code, message, null, httpStatusCode ); - return resultFactory.CreateResult( code, message, null, httpStatusCode ); + return new Result( code, message, null, httpStatusCode, options ); + return resultFactory.CreateResult( code, message, null, httpStatusCode, options ); + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetJsonSerializerOptions( HttpContext context ) { + var factory = context.RequestServices.GetService(); + factory.CheckNull( nameof( factory ) ); + return factory.CreateOptions(); } } \ No newline at end of file diff --git a/src/Util.Application.WebApi/JsonSerializerOptionsFactory.cs b/src/Util.Application.WebApi/JsonSerializerOptionsFactory.cs new file mode 100644 index 000000000..28ce48da5 --- /dev/null +++ b/src/Util.Application.WebApi/JsonSerializerOptionsFactory.cs @@ -0,0 +1,24 @@ +using Util.AspNetCore; +using Util.SystemTextJson; + +namespace Util.Applications; + +/// +/// Json序列化配置工厂 +/// +public class JsonSerializerOptionsFactory : IJsonSerializerOptionsFactory { + /// + /// 创建Json序列化配置 + /// + public JsonSerializerOptions CreateOptions() { + return new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter() + } + }; + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Result.cs b/src/Util.Application.WebApi/Result.cs index ffcf89d1e..cb14d47f5 100644 --- a/src/Util.Application.WebApi/Result.cs +++ b/src/Util.Application.WebApi/Result.cs @@ -26,19 +26,30 @@ public class Result : JsonResult { /// 消息 /// 数据 /// Http状态码 - public Result( string code, string message, dynamic data = null, int? httpStatusCode = null ) : base( null ) { + /// Json序列化配置 + public Result( string code, string message, dynamic data = null, int? httpStatusCode = null, JsonSerializerOptions options = null ) : base( null ) { Code = code; Message = message; Data = data; - SerializerSettings = new JsonSerializerOptions { + SerializerSettings = GetOptions( options ); + StatusCode = httpStatusCode; + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetOptions( JsonSerializerOptions options ) { + if ( options != null ) + return options; + return new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), - Converters = { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { new DateTimeJsonConverter(), new NullableDateTimeJsonConverter() } }; - StatusCode = httpStatusCode; } /// diff --git a/src/Util.Application.WebApi/Usings.cs b/src/Util.Application.WebApi/Usings.cs index 7796c168f..e176120dc 100644 --- a/src/Util.Application.WebApi/Usings.cs +++ b/src/Util.Application.WebApi/Usings.cs @@ -2,32 +2,17 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; global using System.Diagnostics; -global using System.Text; global using System.IO; global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; +global using System.Text.Json.Serialization; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.AspNetCore.Mvc; -global using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; global using Microsoft.AspNetCore.Mvc.Filters; global using Microsoft.Extensions.Localization; global using Microsoft.AspNetCore.Hosting; diff --git a/src/Util.Application/ExceptionExtensions.cs b/src/Util.Application/ExceptionExtensions.cs index ffd80cddd..f575425f9 100644 --- a/src/Util.Application/ExceptionExtensions.cs +++ b/src/Util.Application/ExceptionExtensions.cs @@ -1,70 +1,70 @@ -using Util.Exceptions; -using Util.Properties; - -namespace Util.Applications; - -/// -/// 异常扩展 -/// -public static class ExceptionExtensions { - /// - /// 获取原始异常 - /// - /// 异常 - public static Exception GetRawException( this Exception exception ) { - if( exception == null ) - return null; - if( exception is AspectCore.DynamicProxy.AspectInvocationException aspectInvocationException ) { - if( aspectInvocationException.InnerException == null ) - return aspectInvocationException; - return GetRawException( aspectInvocationException.InnerException ); - } - return exception; - } - - /// - /// 获取异常提示 - /// - /// 异常 - /// 是否生产环境 - public static string GetPrompt( this Exception exception, bool isProduction = false ) { - if( exception == null ) - return null; - exception = exception.GetRawException(); - if( exception == null ) - return null; - if( exception is Warning warning ) - return warning.GetMessage( isProduction ); - return isProduction ? R.SystemError : exception.Message; - } - - /// - /// 获取Http状态码 - /// - /// 异常 - public static int? GetHttpStatusCode( this Exception exception ) { - if ( exception == null ) - return null; - exception = exception.GetRawException(); - if ( exception == null ) - return null; - if ( exception is Warning warning ) - return warning.HttpStatusCode; - return null; - } - - /// - /// 获取错误码 - /// - /// 异常 - public static string GetErrorCode( this Exception exception ) { - if ( exception == null ) - return null; - exception = exception.GetRawException(); - if ( exception == null ) - return null; - if ( exception is Warning warning ) - return warning.Code; - return null; - } +using Util.Exceptions; +using Util.Properties; + +namespace Util.Applications; + +/// +/// 异常扩展 +/// +public static class ExceptionExtensions { + /// + /// 获取原始异常 + /// + /// 异常 + public static Exception GetRawException( this Exception exception ) { + if( exception == null ) + return null; + if( exception is AspectCore.DynamicProxy.AspectInvocationException aspectInvocationException ) { + if( aspectInvocationException.InnerException == null ) + return aspectInvocationException; + return GetRawException( aspectInvocationException.InnerException ); + } + return exception; + } + + /// + /// 获取异常提示 + /// + /// 异常 + /// 是否生产环境 + public static string GetPrompt( this Exception exception, bool isProduction = false ) { + if( exception == null ) + return null; + exception = exception.GetRawException(); + if( exception == null ) + return null; + if( exception is Warning warning ) + return warning.GetMessage( isProduction ); + return isProduction ? R.SystemError : exception.Message; + } + + /// + /// 获取Http状态码 + /// + /// 异常 + public static int? GetHttpStatusCode( this Exception exception ) { + if ( exception == null ) + return null; + exception = exception.GetRawException(); + if ( exception == null ) + return null; + if ( exception is Warning warning ) + return warning.HttpStatusCode; + return null; + } + + /// + /// 获取错误码 + /// + /// 异常 + public static string GetErrorCode( this Exception exception ) { + if ( exception == null ) + return null; + exception = exception.GetRawException(); + if ( exception == null ) + return null; + if ( exception is Warning warning ) + return warning.Code; + return null; + } } \ No newline at end of file diff --git a/src/Util.Application/Usings.cs b/src/Util.Application/Usings.cs index ef15a8d5c..03262834b 100644 --- a/src/Util.Application/Usings.cs +++ b/src/Util.Application/Usings.cs @@ -2,27 +2,8 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; \ No newline at end of file diff --git a/src/Util.AspNetCore/07-Util.AspNetCore.csproj b/src/Util.AspNetCore/07-Util.AspNetCore.csproj index 3a9baff9f..c5a2f42ea 100644 --- a/src/Util.AspNetCore/07-Util.AspNetCore.csproj +++ b/src/Util.AspNetCore/07-Util.AspNetCore.csproj @@ -18,12 +18,6 @@ .\obj\Release\$(NetTargetFramework)\Util.AspNetCore.xml - - - - - - True diff --git a/src/Util.AspNetCore/AspNetCore/IJsonSerializerOptionsFactory.cs b/src/Util.AspNetCore/AspNetCore/IJsonSerializerOptionsFactory.cs new file mode 100644 index 000000000..0c5936878 --- /dev/null +++ b/src/Util.AspNetCore/AspNetCore/IJsonSerializerOptionsFactory.cs @@ -0,0 +1,13 @@ +using Util.Dependency; + +namespace Util.AspNetCore; + +/// +/// Json序列化配置工厂 +/// +public interface IJsonSerializerOptionsFactory : ISingletonDependency { + /// + /// 创建Json序列化配置 + /// + JsonSerializerOptions CreateOptions(); +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Http/HttpRequest.cs b/src/Util.AspNetCore/Http/HttpRequest.cs index 65048d5f5..44045ebb4 100644 --- a/src/Util.AspNetCore/Http/HttpRequest.cs +++ b/src/Util.AspNetCore/Http/HttpRequest.cs @@ -1,4 +1,6 @@ -using Util.Helpers; +using System.Text.Encodings.Web; +using System.Text.Unicode; +using Util.Helpers; using Util.SystemTextJson; namespace Util.Http; @@ -67,6 +69,10 @@ public HttpRequest( IHttpClientFactory httpClientFactory, HttpClient httpClient, #region 属性 + /// + /// 基地址 + /// + protected string BaseAddressUri { get; private set; } /// /// 证书路径 /// @@ -116,6 +122,10 @@ public HttpRequest( IHttpClientFactory httpClientFactory, HttpClient httpClient, /// protected Action SuccessAction { get; private set; } /// + /// 执行成功操作 + /// + protected Func SuccessFunc { get; private set; } + /// /// 执行失败操作 /// protected Action FailAction { get; private set; } @@ -126,7 +136,7 @@ public HttpRequest( IHttpClientFactory httpClientFactory, HttpClient httpClient, #endregion - #region HttpClientName(Http客户端名称) + #region HttpClientName(设置Http客户端名称) /// public IHttpRequest HttpClientName( string name ) { @@ -136,6 +146,19 @@ public IHttpRequest HttpClientName( string name ) { #endregion + #region BaseAddress(设置基地址) + + /// + /// 设置基地址 + /// + /// 基地址 + public IHttpRequest BaseAddress( string baseAddress ) { + BaseAddressUri = baseAddress; + return this; + } + + #endregion + #region ContentType(设置内容类型) /// @@ -166,6 +189,16 @@ public IHttpRequest Encoding( Encoding encoding ) { #endregion + #region BearerToken(设置访问令牌) + + /// + public IHttpRequest BearerToken( string token ) { + Header( "Authorization", $"Bearer {token}" ); + return this; + } + + #endregion + #region Certificate(设置证书) /// @@ -187,6 +220,28 @@ public IHttpRequest JsonSerializerOptions( JsonSerializerOptions option #endregion + #region GetJsonSerializerOptions(获取Json序列化配置) + + /// + /// 获取Json序列化配置 + /// + protected virtual JsonSerializerOptions GetJsonSerializerOptions() { + if ( _jsonSerializerOptions != null ) + return _jsonSerializerOptions; + return new JsonSerializerOptions { + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter() + } + }; + } + + #endregion + #region Header(设置请求头) /// @@ -333,6 +388,15 @@ public IHttpRequest OnSuccess( Action action ) { return this; } + /// + /// 请求成功事件 + /// + /// 执行成功操作,参数为响应结果 + public IHttpRequest OnSuccess( Func action ) { + SuccessFunc = action; + return this; + } + #endregion #region OnFail(请求失败事件) @@ -358,11 +422,11 @@ public IHttpRequest OnComplete( Action act #region GetResultAsync(获取结果) /// - public async Task GetResultAsync() { + public async Task GetResultAsync( CancellationToken cancellationToken = default ) { var message = CreateMessage(); if( SendBefore( message ) == false ) return default; - var response = await SendAsync( message ); + var response = await SendAsync( message, cancellationToken ); return await SendAfterAsync( response ); } @@ -469,21 +533,6 @@ private string GetJsonContentValue() { return null; } - /// - /// 获取Json序列化配置 - /// - protected virtual JsonSerializerOptions GetJsonSerializerOptions() { - if ( _jsonSerializerOptions != null ) - return _jsonSerializerOptions; - return new JsonSerializerOptions { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - Converters = { - new DateTimeJsonConverter(), - new NullableDateTimeJsonConverter() - } - }; - } - /// /// 创建xml内容 /// @@ -513,9 +562,12 @@ protected virtual bool SendBefore( HttpRequestMessage message ) { /// 发送请求 /// /// 请求消息 - protected async Task SendAsync( HttpRequestMessage message ) { + /// 取消令牌 + protected async Task SendAsync( HttpRequestMessage message, CancellationToken cancellationToken ) { var client = GetClient(); - return await client.SendAsync( message ); + client.CheckNull( nameof( client ) ); + InitHttpClient( client ); + return await client.SendAsync( message, cancellationToken ); } #endregion @@ -530,9 +582,7 @@ protected HttpClient GetClient() { return _httpClient; var clientHandler = CreateHttpClientHandler(); InitHttpClientHandler( clientHandler ); - if( _httpClientName.IsEmpty() ) - return _httpClientFactory.CreateClient(); - return _httpClientFactory.CreateClient( _httpClientName ); + return _httpClientName.IsEmpty() ? _httpClientFactory.CreateClient() : _httpClientFactory.CreateClient( _httpClientName ); } /// @@ -541,7 +591,7 @@ protected HttpClient GetClient() { protected HttpClientHandler CreateHttpClientHandler() { var handlerFactory = _httpClientFactory as IHttpMessageHandlerFactory; var handler = handlerFactory?.CreateHandler(); - while( handler is DelegatingHandler delegatingHandler ) { + while ( handler is DelegatingHandler delegatingHandler ) { handler = delegatingHandler.InnerHandler; } return handler as HttpClientHandler; @@ -564,15 +614,40 @@ protected virtual void InitHttpClientHandler( HttpClientHandler handler ) { /// /// 初始化证书 /// - protected void InitCertificate( HttpClientHandler handler ) { + protected virtual void InitCertificate( HttpClientHandler handler ) { if( CertificatePath.IsEmpty() ) return; var certificate = new X509Certificate2( CertificatePath, CertificatePassword, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet ); + handler.ClientCertificates.Clear(); handler.ClientCertificates.Add( certificate ); } #endregion + #region InitHttpClient(初始化Http客户端) + + /// + /// 初始化Http客户端 + /// + protected virtual void InitHttpClient( HttpClient client ) { + InitBaseAddress( client ); + } + + #endregion + + #region InitBaseAddress(初始化基地址) + + /// + /// 初始化基地址 + /// + protected virtual void InitBaseAddress( HttpClient client ) { + if ( BaseAddressUri.IsEmpty() ) + return; + client.BaseAddress = new Uri( BaseAddressUri ); + } + + #endregion + #region SendAfterAsync(发送后操作) /// @@ -586,7 +661,7 @@ protected virtual async Task SendAfterAsync( HttpResponseMessage respon try { content = await response.Content.ReadAsStringAsync(); if( response.IsSuccessStatusCode ) - return SuccessHandler( response, content ); + return await SuccessHandlerAsync( response, content ); FailHandler( response, content ); return null; } @@ -602,8 +677,11 @@ protected virtual async Task SendAfterAsync( HttpResponseMessage respon /// /// 成功处理操作 /// - protected virtual TResult SuccessHandler( HttpResponseMessage response, string content ) { - TResult result = ConvertTo( content, response.GetContentType() ); + protected virtual async Task SuccessHandlerAsync( HttpResponseMessage response, string content ) { + var result = ConvertTo( content, response.GetContentType() ); + SuccessAction?.Invoke( result ); + if ( SuccessFunc != null ) + await SuccessFunc( result ); return result; } @@ -621,17 +699,7 @@ protected virtual TResult ConvertTo( string content, string contentType ) { return ConvertAction( content ); if( typeof( TResult ) == typeof( string ) ) return (TResult)(object)content; - if( contentType.SafeString().ToLower() == "application/json" ) { - var options = new JsonSerializerOptions { - PropertyNameCaseInsensitive = true, - Converters = { - new DateTimeJsonConverter(), - new NullableDateTimeJsonConverter() - } - }; - return Json.ToObject( content, options ); - } - return null; + return contentType.SafeString().ToLower() == "application/json" ? Json.ToObject( content, GetJsonSerializerOptions() ) : null; } #endregion @@ -661,11 +729,11 @@ protected virtual void CompleteHandler( HttpResponseMessage response, object con #region GetStreamAsync(获取流) /// - public async Task GetStreamAsync() { + public async Task GetStreamAsync( CancellationToken cancellationToken = default ) { var message = CreateMessage(); if( SendBefore( message ) == false ) return default; - var response = await SendAsync( message ); + var response = await SendAsync( message, cancellationToken ); return await GetStream( response ); } @@ -692,8 +760,8 @@ protected virtual async Task GetStream( HttpResponseMessage response ) { #region WriteAsync(写入文件) /// - public async Task WriteAsync( string filePath ) { - var bytes = await GetStreamAsync(); + public async Task WriteAsync( string filePath, CancellationToken cancellationToken = default ) { + var bytes = await GetStreamAsync( cancellationToken ); await Util.Helpers.File.WriteAsync( filePath, bytes ); } diff --git a/src/Util.AspNetCore/Usings.cs b/src/Util.AspNetCore/Usings.cs index c7f913425..4ccdbdcc0 100644 --- a/src/Util.AspNetCore/Usings.cs +++ b/src/Util.AspNetCore/Usings.cs @@ -3,24 +3,11 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; global using System.Text; global using System.IO; -global using System.Reflection; -global using System.ComponentModel; global using System.Globalization; global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Security.Cryptography; -global using System.Security.Principal; global using System.Net.NetworkInformation; global using System.Net.Sockets; global using System.Net; @@ -30,11 +17,7 @@ global using Microsoft.AspNetCore.Hosting; global using Microsoft.AspNetCore.Http; global using Microsoft.AspNetCore.Http.Extensions; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.AspNetCore.WebUtilities; global using Microsoft.AspNetCore.Authorization; diff --git a/src/Util.Caching.EasyCaching/Usings.cs b/src/Util.Caching.EasyCaching/Usings.cs index fb2d33ac0..ef479070b 100644 --- a/src/Util.Caching.EasyCaching/Usings.cs +++ b/src/Util.Caching.EasyCaching/Usings.cs @@ -3,27 +3,10 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; global using EasyCaching.Core; global using EasyCaching.Core.Interceptor; diff --git a/src/Util.Caching/Usings.cs b/src/Util.Caching/Usings.cs index e5f9b8dc5..4c6502951 100644 --- a/src/Util.Caching/Usings.cs +++ b/src/Util.Caching/Usings.cs @@ -3,26 +3,6 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using AspectCore.DynamicProxy; \ No newline at end of file diff --git a/src/Util.Core/Exceptions/Warning.cs b/src/Util.Core/Exceptions/Warning.cs index 0582a5346..a3d90b6c6 100644 --- a/src/Util.Core/Exceptions/Warning.cs +++ b/src/Util.Core/Exceptions/Warning.cs @@ -16,8 +16,8 @@ public Warning( Exception exception ) /// 初始化应用程序异常 /// /// 错误消息 - /// 错误码 /// 异常 + /// 错误码 /// Http状态码 public Warning( string message, Exception exception = null, string code = null, int? httpStatusCode = null ) : base( message ?? "", exception ) { diff --git a/src/Util.Core/Helpers/Common.cs b/src/Util.Core/Helpers/Common.cs index 8ca2246dd..9f52cdb9a 100644 --- a/src/Util.Core/Helpers/Common.cs +++ b/src/Util.Core/Helpers/Common.cs @@ -71,7 +71,14 @@ public static string GetCurrentDirectory() { /// /// 获取当前目录的上级路径 /// - public static string GetParentDirectory() { - return Directory.GetParent( Directory.GetCurrentDirectory() )?.FullName; + /// 向上钻取的深度 + public static string GetParentDirectory( int depth = 1 ) { + var path = Directory.GetCurrentDirectory(); + for ( int i = 0; i < depth; i++ ) { + var parent = Directory.GetParent( path ); + if ( parent is { Exists: true } ) + path = parent.FullName; + } + return path; } } \ No newline at end of file diff --git a/src/Util.Core/Helpers/Config.cs b/src/Util.Core/Helpers/Config.cs index ec867fa7c..26ea7278b 100644 --- a/src/Util.Core/Helpers/Config.cs +++ b/src/Util.Core/Helpers/Config.cs @@ -66,14 +66,14 @@ public static IConfiguration CreateConfiguration( string basePath = null,params basePath ??= Common.ApplicationBaseDirectory; var builder = new ConfigurationBuilder() .SetBasePath( basePath ) - .AddJsonFile( "appsettings.json", true, true ); + .AddJsonFile( "appsettings.json", true, false ); var environment = Environment.GetEnvironmentName(); if ( environment.IsEmpty() == false ) - builder.AddJsonFile( $"appsettings.{environment}.json", true, true ); + builder.AddJsonFile( $"appsettings.{environment}.json", true, false ); if ( jsonFiles == null ) return builder.Build(); foreach ( var file in jsonFiles ) - builder.AddJsonFile( file, true, true ); + builder.AddJsonFile( file, true, false ); return builder.Build(); } diff --git a/src/Util.Core/Helpers/Convert.cs b/src/Util.Core/Helpers/Convert.cs index 978408fa5..37ce66630 100644 --- a/src/Util.Core/Helpers/Convert.cs +++ b/src/Util.Core/Helpers/Convert.cs @@ -313,7 +313,7 @@ public static T To( object input ) { return default; if( input is string && string.IsNullOrWhiteSpace( input.ToString() ) ) return default; - Type type = Common.GetType(); + var type = Common.GetType(); var typeName = type.Name.ToUpperInvariant(); try { if( typeName == "STRING" || typeName == "GUID" ) @@ -342,11 +342,11 @@ public static T To( object input ) { /// /// 对象 public static IDictionary ToDictionary( object data ) { - if( data == null ) - return null; + var result = new Dictionary(); + if ( data == null ) + return result; if ( data is IEnumerable> dic ) return new Dictionary( dic ); - var result = new Dictionary(); foreach ( PropertyDescriptor property in TypeDescriptor.GetProperties( data ) ) { var value = property.GetValue( data ); result.Add( property.Name, value ); diff --git a/src/Util.Core/Helpers/Json.cs b/src/Util.Core/Helpers/Json.cs index dbc23e08f..50e51df9c 100644 --- a/src/Util.Core/Helpers/Json.cs +++ b/src/Util.Core/Helpers/Json.cs @@ -12,11 +12,22 @@ public static class Json { /// 目标对象 /// Json配置 public static string ToJson( T value, JsonOptions options ) { - options ??= new JsonOptions(); + if ( options == null ) + return ToJson( value ); + var jsonSerializerOptions = ToJsonSerializerOptions( options ); + return ToJson( value, jsonSerializerOptions, options.RemoveQuotationMarks, options.ToSingleQuotes, options.IgnoreInterface ); + } + + /// + /// 转换序列化配置 + /// + private static JsonSerializerOptions ToJsonSerializerOptions( JsonOptions options ) { var jsonSerializerOptions = new JsonSerializerOptions(); if ( options.IgnoreNullValues ) jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; - return ToJson( value, jsonSerializerOptions, options.RemoveQuotationMarks, options.ToSingleQuotes ); + if ( options.IgnoreCase ) + jsonSerializerOptions.PropertyNameCaseInsensitive = true; + return jsonSerializerOptions; } /// @@ -27,12 +38,19 @@ public static string ToJson( T value, JsonOptions options ) { /// 是否移除双引号 /// 是否将双引号转成单引号 public static string ToJson( T value, JsonSerializerOptions options = null,bool removeQuotationMarks = false, bool toSingleQuotes = false ) { + return ToJson( value, options, removeQuotationMarks, toSingleQuotes,true ); + } + + /// + /// 将对象转换为Json字符串 + /// + private static string ToJson( T value, JsonSerializerOptions options, bool removeQuotationMarks, bool toSingleQuotes,bool ignoreInterface ) { if ( value == null ) return string.Empty; options = GetToJsonOptions( options ); - var result = JsonSerializer.Serialize( value, options ); + var result = Serialize( value, options, ignoreInterface ); if ( removeQuotationMarks ) - result = result.Replace( "\"","" ); + result = result.Replace( "\"", "" ); if ( toSingleQuotes ) result = result.Replace( "\"", "'" ); return result; @@ -53,6 +71,18 @@ private static JsonSerializerOptions GetToJsonOptions( JsonSerializerOptions opt }; } + /// + /// Json序列化 + /// + private static string Serialize( T value, JsonSerializerOptions options, bool ignoreInterface ) { + if ( ignoreInterface ) { + object instance = value; + if( instance != null ) + return JsonSerializer.Serialize( instance, options ); + } + return JsonSerializer.Serialize( value, options ); + } + /// /// 将对象转换为Json字符串 /// @@ -70,6 +100,19 @@ private static JsonSerializerOptions GetToJsonOptions( JsonSerializerOptions opt return await reader.ReadToEndAsync( cancellationToken ); } + /// + /// 将Json字符串转换为对象 + /// + /// Json字符串 + /// 序列化配置 + public static T ToObject( string json, JsonOptions options ) { + if ( string.IsNullOrWhiteSpace( json ) ) + return default; + if ( options == null ) + return ToObject( json ); + return ToObject( json, ToJsonSerializerOptions( options ) ); + } + /// /// 将Json字符串转换为对象 /// @@ -117,9 +160,9 @@ private static JsonSerializerOptions GetToObjectOptions( JsonSerializerOptions o /// /// Json字符串 /// 序列化配置 - /// 取消令牌 /// Json字符编码,默认UTF8 - public static async Task ToObjectAsync( string json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default,Encoding encoding = null ) { + /// 取消令牌 + public static async Task ToObjectAsync( string json, JsonSerializerOptions options = null, Encoding encoding = null, CancellationToken cancellationToken = default ) { if ( string.IsNullOrWhiteSpace( json ) ) return default; encoding ??= Encoding.UTF8; diff --git a/src/Util.Core/Helpers/Xml.Builder.cs b/src/Util.Core/Helpers/Xml.Builder.cs new file mode 100644 index 000000000..2f2308630 --- /dev/null +++ b/src/Util.Core/Helpers/Xml.Builder.cs @@ -0,0 +1,95 @@ +namespace Util.Helpers; + +/// +/// Xml操作 - 生成器 +/// +public partial class Xml { + /// + /// 初始化Xml操作 + /// + /// Xml字符串 + public Xml( string xml = null ) { + Document = new XmlDocument(); + Document.LoadXml( GetXml( xml ) ); + Root = Document.DocumentElement; + if( Root == null ) + throw new ArgumentException( nameof( xml ) ); + } + + /// + /// 获取Xml字符串 + /// + private string GetXml( string xml ) { + return string.IsNullOrWhiteSpace( xml ) ? "" : xml; + } + + /// + /// Xml文档 + /// + public XmlDocument Document { get; } + + /// + /// Xml根节点 + /// + public XmlElement Root { get; } + + /// + /// 添加节点 + /// + /// 节点名称 + /// 值 + /// 父节点 + public XmlNode AddNode( string name, object value = null, XmlNode parent = null ) { + var node = CreateNode( name, value, XmlNodeType.Element ); + GetParent( parent ).AppendChild( node ); + return node; + } + + /// + /// 创建节点 + /// + private XmlNode CreateNode( string name, object value, XmlNodeType type ) { + var node = Document.CreateNode( type, name, string.Empty ); + if( string.IsNullOrWhiteSpace( value.SafeString() ) == false ) + node.InnerText = value.SafeString(); + return node; + } + + /// + /// 获取父节点 + /// + private XmlNode GetParent( XmlNode parent ) { + if( parent == null ) + return Root; + return parent; + } + + /// + /// 添加CDATA节点 + /// + /// 值 + /// 父节点 + public XmlNode AddCDataNode( object value, XmlNode parent = null ) { + var node = CreateNode( Id.Create(), value, XmlNodeType.CDATA ); + GetParent( parent ).AppendChild( node ); + return node; + } + + /// + /// 添加CDATA节点 + /// + /// 值 + /// 父节点名称 + public XmlNode AddCDataNode( object value, string parentName ) { + var parent = CreateNode( parentName, null, XmlNodeType.Element ); + Root.AppendChild( parent ); + return AddCDataNode( value, parent ); + } + + /// + /// 输出Xml + /// + public override string ToString() { + return Document.OuterXml; + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Xml.Tools.cs b/src/Util.Core/Helpers/Xml.Tools.cs new file mode 100644 index 000000000..7bc9e9247 --- /dev/null +++ b/src/Util.Core/Helpers/Xml.Tools.cs @@ -0,0 +1,61 @@ +namespace Util.Helpers; + +/// +/// Xml操作 - 工具 +/// +public partial class Xml { + /// + /// 将Xml字符串转换为XDocument + /// + /// Xml字符串 + public static XDocument ToDocument( string xml ) { + return XDocument.Parse( xml ); + } + + /// + /// 将Xml字符串转换为XElement列表 + /// + /// Xml字符串 + public static List ToElements( string xml ) { + var document = ToDocument( xml ); + if( document?.Root == null ) + return new List(); + return document.Root.Elements().ToList(); + } + + /// + /// 加载Xml文件到XDocument + /// + /// Xml文件绝对路径 + public static async Task LoadFileToDocumentAsync( string filePath ) { + return await LoadFileToDocumentAsync( filePath, Encoding.UTF8 ); + } + + /// + /// 加载Xml文件到XDocument + /// + /// Xml文件绝对路径 + /// 字符编码 + public static async Task LoadFileToDocumentAsync( string filePath,Encoding encoding ) { + var xml = await Util.Helpers.File.ReadToStringAsync( filePath, encoding ); + return ToDocument( xml ); + } + + /// + /// 加载Xml文件到XElement列表 + /// + /// Xml文件绝对路径 + public static async Task> LoadFileToElementsAsync( string filePath ) { + return await LoadFileToElementsAsync( filePath, Encoding.UTF8 ); + } + + /// + /// 加载Xml文件到XElement列表 + /// + /// Xml文件绝对路径 + /// 字符编码 + public static async Task> LoadFileToElementsAsync( string filePath, Encoding encoding ) { + var xml = await Util.Helpers.File.ReadToStringAsync( filePath, encoding ); + return ToElements( xml ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Http/IHttpRequest.cs b/src/Util.Core/Http/IHttpRequest.cs index 1a2b2e5f7..563ed7343 100644 --- a/src/Util.Core/Http/IHttpRequest.cs +++ b/src/Util.Core/Http/IHttpRequest.cs @@ -17,6 +17,11 @@ public interface IHttpRequest : IHttpRequest where TResult : class { /// HttpClient名称 IHttpRequest HttpClientName( string name ); /// + /// 设置基地址 + /// + /// 基地址 + IHttpRequest BaseAddress( string baseAddress ); + /// /// 设置字符编码 /// /// 字符编码,范例:gb2312 @@ -27,6 +32,11 @@ public interface IHttpRequest : IHttpRequest where TResult : class { /// 字符编码 IHttpRequest Encoding( Encoding encoding ); /// + /// 设置访问令牌 + /// + /// 访问令牌 + IHttpRequest BearerToken( string token ); + /// /// 设置内容类型 /// /// 内容类型 @@ -121,6 +131,11 @@ public interface IHttpRequest : IHttpRequest where TResult : class { /// 执行成功操作,参数为响应结果 IHttpRequest OnSuccess( Action action ); /// + /// 请求成功事件 + /// + /// 执行成功操作,参数为响应结果 + IHttpRequest OnSuccess( Func action ); + /// /// 请求失败事件 /// /// 执行失败操作,参数为响应消息和响应内容 @@ -133,14 +148,17 @@ public interface IHttpRequest : IHttpRequest where TResult : class { /// /// 获取结果 /// - Task GetResultAsync(); + /// 取消令牌 + Task GetResultAsync( CancellationToken cancellationToken = default ); /// /// 获取流 /// - Task GetStreamAsync(); + /// 取消令牌 + Task GetStreamAsync( CancellationToken cancellationToken = default ); /// /// 写入文件 /// /// 文件绝对路径 - Task WriteAsync( string filePath ); + /// 取消令牌 + Task WriteAsync( string filePath, CancellationToken cancellationToken = default ); } \ No newline at end of file diff --git a/src/Util.Core/JsonOptions.cs b/src/Util.Core/JsonOptions.cs index c7ca8f5bb..2eeadc61a 100644 --- a/src/Util.Core/JsonOptions.cs +++ b/src/Util.Core/JsonOptions.cs @@ -4,6 +4,15 @@ /// Json配置 /// public class JsonOptions { + /// + /// 初始化Json配置 + /// + public JsonOptions() { + IgnoreNullValues = true; + IgnoreCase = true; + IgnoreInterface = true; + } + /// /// 是否移除双引号,默认值: false /// @@ -13,7 +22,15 @@ public class JsonOptions { /// public bool ToSingleQuotes { get; set; } /// - /// 是否忽略null值,默认值: false + /// 是否忽略null值,默认值: true /// public bool IgnoreNullValues { get; set; } + /// + /// 反序列化时是否忽略大小写,默认值: true + /// + public bool IgnoreCase { get; set; } + /// + /// 反序列化接口实例时是否忽略接口,按实现类进行序列化,默认值: true + /// + public bool IgnoreInterface { get; set; } } \ No newline at end of file diff --git a/src/Util.Core/Properties/R.Designer.cs b/src/Util.Core/Properties/R.Designer.cs index 7d6fe3daf..215fd508e 100644 --- a/src/Util.Core/Properties/R.Designer.cs +++ b/src/Util.Core/Properties/R.Designer.cs @@ -231,6 +231,15 @@ public static string TypeNotEnum { } } + /// + /// 查找类似 您没有该操作的权限 的本地化字符串。 + /// + public static string UnauthorizedMessage { + get { + return ResourceManager.GetString("UnauthorizedMessage", resourceCulture); + } + } + /// /// 查找类似 上传 的本地化字符串。 /// diff --git a/src/Util.Core/Properties/R.resx b/src/Util.Core/Properties/R.resx index 5527b167e..e584c26a8 100644 --- a/src/Util.Core/Properties/R.resx +++ b/src/Util.Core/Properties/R.resx @@ -175,6 +175,9 @@ 类型 {0} 不是枚举 + + 您没有该操作的权限 + 上传 diff --git a/src/Util.Core/Usings.cs b/src/Util.Core/Usings.cs index 536708a62..689e28fa2 100644 --- a/src/Util.Core/Usings.cs +++ b/src/Util.Core/Usings.cs @@ -19,8 +19,9 @@ global using System.Text.Unicode; global using System.Buffers; global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; global using System.Net.Http; +global using System.Xml; +global using System.Xml.Linq; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; diff --git a/src/Util.Core/ValidationExtensions.cs b/src/Util.Core/ValidationExtensions.cs index 7802d04f2..fc0a48baa 100644 --- a/src/Util.Core/ValidationExtensions.cs +++ b/src/Util.Core/ValidationExtensions.cs @@ -1,4 +1,5 @@ -namespace Util; +#nullable enable +namespace Util; /// /// 验证扩展 @@ -44,7 +45,7 @@ public static bool IsEmpty( [NotNullWhen( false )] this Guid? value ) { /// 是否为空 /// /// 值 - public static bool IsEmpty( this IEnumerable value ) { + public static bool IsEmpty( this IEnumerable? value ) { if( value == null ) return true; return !value.Any(); diff --git a/src/Util.Data.Abstractions/Usings.cs b/src/Util.Data.Abstractions/Usings.cs index 82845070f..6eb207db5 100644 --- a/src/Util.Data.Abstractions/Usings.cs +++ b/src/Util.Data.Abstractions/Usings.cs @@ -3,28 +3,4 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Security.Cryptography; -global using System.Security.Claims; -global using System.Security.Principal; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file +global using System.Linq.Expressions; \ No newline at end of file diff --git a/src/Util.Data.Core/Usings.cs b/src/Util.Data.Core/Usings.cs index 05071e106..5ad6166fa 100644 --- a/src/Util.Data.Core/Usings.cs +++ b/src/Util.Data.Core/Usings.cs @@ -2,29 +2,7 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; global using System.Data; global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; global using System.Linq.Expressions; -global using System.Text.RegularExpressions; global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.Data.Dapper.All/Usings.cs b/src/Util.Data.Dapper.All/Usings.cs index eff011f74..3e51ec473 100644 --- a/src/Util.Data.Dapper.All/Usings.cs +++ b/src/Util.Data.Dapper.All/Usings.cs @@ -1,31 +1,2 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; -global using Dapper; diff --git a/src/Util.Data.Dapper.Core/Usings.cs b/src/Util.Data.Dapper.Core/Usings.cs index eff011f74..14a52a9ff 100644 --- a/src/Util.Data.Dapper.Core/Usings.cs +++ b/src/Util.Data.Dapper.Core/Usings.cs @@ -2,30 +2,13 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; global using System.Text; -global using System.IO; global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; global using System.Text.Encodings.Web; global using System.Text.Json.Serialization; global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Dapper; diff --git a/src/Util.Data.Dapper.MySql/Usings.cs b/src/Util.Data.Dapper.MySql/Usings.cs index 14f159f4c..75d67f651 100644 --- a/src/Util.Data.Dapper.MySql/Usings.cs +++ b/src/Util.Data.Dapper.MySql/Usings.cs @@ -2,31 +2,8 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; global using System.Text; -global using System.IO; global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; -global using Dapper; global using MySqlConnector; diff --git a/src/Util.Data.Dapper.PostgreSql/Usings.cs b/src/Util.Data.Dapper.PostgreSql/Usings.cs index 46153acee..6d83fa6b9 100644 --- a/src/Util.Data.Dapper.PostgreSql/Usings.cs +++ b/src/Util.Data.Dapper.PostgreSql/Usings.cs @@ -2,31 +2,8 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; global using System.Text; -global using System.IO; global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; -global using Dapper; global using Npgsql; diff --git a/src/Util.Data.Dapper.SqlServer/Usings.cs b/src/Util.Data.Dapper.SqlServer/Usings.cs index 995f832bb..68c543f15 100644 --- a/src/Util.Data.Dapper.SqlServer/Usings.cs +++ b/src/Util.Data.Dapper.SqlServer/Usings.cs @@ -2,31 +2,8 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; global using System.Text; -global using System.IO; global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.Data.SqlClient; -global using Dapper; diff --git a/src/Util.Data.EntityFrameworkCore.MySql/Usings.cs b/src/Util.Data.EntityFrameworkCore.MySql/Usings.cs index 4be7c681d..4a26a3949 100644 --- a/src/Util.Data.EntityFrameworkCore.MySql/Usings.cs +++ b/src/Util.Data.EntityFrameworkCore.MySql/Usings.cs @@ -1,39 +1,6 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; global using System.Data.Common; -global using System.Buffers; -global using System.Collections; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using System.ComponentModel.DataAnnotations.Schema; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.EntityFrameworkCore; -global using Microsoft.EntityFrameworkCore.ChangeTracking; -global using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -global using Microsoft.EntityFrameworkCore.Metadata; global using Microsoft.EntityFrameworkCore.Infrastructure; global using MySqlConnector; diff --git a/src/Util.Data.EntityFrameworkCore.Oracle/Usings.cs b/src/Util.Data.EntityFrameworkCore.Oracle/Usings.cs index 7d294fc60..797d4972e 100644 --- a/src/Util.Data.EntityFrameworkCore.Oracle/Usings.cs +++ b/src/Util.Data.EntityFrameworkCore.Oracle/Usings.cs @@ -1,39 +1,6 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; global using System.Data.Common; -global using System.Buffers; -global using System.Collections; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using System.ComponentModel.DataAnnotations.Schema; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.EntityFrameworkCore; -global using Microsoft.EntityFrameworkCore.ChangeTracking; -global using Microsoft.EntityFrameworkCore.Storage.ValueConversion; global using Microsoft.EntityFrameworkCore.Metadata; -global using Microsoft.EntityFrameworkCore.Infrastructure; global using Oracle.EntityFrameworkCore.Infrastructure; \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.PostgreSql/Usings.cs b/src/Util.Data.EntityFrameworkCore.PostgreSql/Usings.cs index ed2a33657..80c67f2ec 100644 --- a/src/Util.Data.EntityFrameworkCore.PostgreSql/Usings.cs +++ b/src/Util.Data.EntityFrameworkCore.PostgreSql/Usings.cs @@ -1,39 +1,5 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; global using System.Data.Common; -global using System.Buffers; -global using System.Collections; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using System.ComponentModel.DataAnnotations.Schema; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.EntityFrameworkCore; -global using Microsoft.EntityFrameworkCore.ChangeTracking; -global using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -global using Microsoft.EntityFrameworkCore.Metadata; -global using Microsoft.EntityFrameworkCore.Infrastructure; global using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.SqlServer/02-Util.Data.EntityFrameworkCore.SqlServer.csproj b/src/Util.Data.EntityFrameworkCore.SqlServer/02-Util.Data.EntityFrameworkCore.SqlServer.csproj index fb46520e6..28b2c350e 100644 --- a/src/Util.Data.EntityFrameworkCore.SqlServer/02-Util.Data.EntityFrameworkCore.SqlServer.csproj +++ b/src/Util.Data.EntityFrameworkCore.SqlServer/02-Util.Data.EntityFrameworkCore.SqlServer.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/Util.Data.EntityFrameworkCore.SqlServer/Usings.cs b/src/Util.Data.EntityFrameworkCore.SqlServer/Usings.cs index 8bbcd04d1..343815cfa 100644 --- a/src/Util.Data.EntityFrameworkCore.SqlServer/Usings.cs +++ b/src/Util.Data.EntityFrameworkCore.SqlServer/Usings.cs @@ -1,38 +1,6 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; global using System.Data.Common; -global using System.Buffers; -global using System.Collections; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using System.ComponentModel.DataAnnotations.Schema; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.EntityFrameworkCore; -global using Microsoft.EntityFrameworkCore.ChangeTracking; -global using Microsoft.EntityFrameworkCore.Storage.ValueConversion; global using Microsoft.EntityFrameworkCore.Metadata; global using Microsoft.EntityFrameworkCore.Infrastructure; \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.Sqlite/05-Util.Data.EntityFrameworkCore.Sqlite.csproj b/src/Util.Data.EntityFrameworkCore.Sqlite/05-Util.Data.EntityFrameworkCore.Sqlite.csproj index 994088f95..611f30679 100644 --- a/src/Util.Data.EntityFrameworkCore.Sqlite/05-Util.Data.EntityFrameworkCore.Sqlite.csproj +++ b/src/Util.Data.EntityFrameworkCore.Sqlite/05-Util.Data.EntityFrameworkCore.Sqlite.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/Util.Data.EntityFrameworkCore.Sqlite/Usings.cs b/src/Util.Data.EntityFrameworkCore.Sqlite/Usings.cs index 8bbcd04d1..731033ee7 100644 --- a/src/Util.Data.EntityFrameworkCore.Sqlite/Usings.cs +++ b/src/Util.Data.EntityFrameworkCore.Sqlite/Usings.cs @@ -1,38 +1,5 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; global using System.Data.Common; -global using System.Buffers; -global using System.Collections; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using System.ComponentModel.DataAnnotations.Schema; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.EntityFrameworkCore; -global using Microsoft.EntityFrameworkCore.ChangeTracking; -global using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -global using Microsoft.EntityFrameworkCore.Metadata; global using Microsoft.EntityFrameworkCore.Infrastructure; \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/01-Util.Data.EntityFrameworkCore.csproj b/src/Util.Data.EntityFrameworkCore/01-Util.Data.EntityFrameworkCore.csproj index 9174285da..c152a9ece 100644 --- a/src/Util.Data.EntityFrameworkCore/01-Util.Data.EntityFrameworkCore.csproj +++ b/src/Util.Data.EntityFrameworkCore/01-Util.Data.EntityFrameworkCore.csproj @@ -27,8 +27,8 @@ - - + + diff --git a/src/Util.Data.EntityFrameworkCore/UnitOfWorkBase.cs b/src/Util.Data.EntityFrameworkCore/UnitOfWorkBase.cs index 825033d0f..d86f97033 100644 --- a/src/Util.Data.EntityFrameworkCore/UnitOfWorkBase.cs +++ b/src/Util.Data.EntityFrameworkCore/UnitOfWorkBase.cs @@ -538,32 +538,32 @@ protected virtual void AddEntityDeletedEvent( object entity ) { /// 保存后操作 /// protected virtual async Task SaveChangesAfter() { - await PublishEventsAsync(); await ExecuteActionsAsync(); + await PublishEventsAsync(); } #endregion - #region PublishEventsAsync(发布事件) + #region ExecuteActionsAsync(执行工作单元操作集合) /// - /// 发布事件 + /// 执行工作单元操作集合 /// - protected virtual async Task PublishEventsAsync() { - var events = new List( Events ); - Events.Clear(); - await EventBus.PublishAsync( events ); + protected virtual async Task ExecuteActionsAsync() { + await ActionManager.ExecuteAsync(); } #endregion - #region ExecuteActionsAsync(执行工作单元操作集合) + #region PublishEventsAsync(发布事件) /// - /// 执行工作单元操作集合 + /// 发布事件 /// - protected virtual async Task ExecuteActionsAsync() { - await ActionManager.ExecuteAsync(); + protected virtual async Task PublishEventsAsync() { + var events = new List( Events ); + Events.Clear(); + await EventBus.PublishAsync( events ); } #endregion diff --git a/src/Util.Data.EntityFrameworkCore/UnitOfWorkExtensions.cs b/src/Util.Data.EntityFrameworkCore/UnitOfWorkExtensions.cs index 67a32835b..c87450ad7 100644 --- a/src/Util.Data.EntityFrameworkCore/UnitOfWorkExtensions.cs +++ b/src/Util.Data.EntityFrameworkCore/UnitOfWorkExtensions.cs @@ -10,7 +10,7 @@ public static class UnitOfWorkExtensions { /// 工作单元 public static async Task> GetAppliedMigrationsAsync( this IUnitOfWork source ) { source.CheckNull( nameof( source ) ); - if ( source is not UnitOfWorkBase unitOfWork ) + if ( source is not DbContext unitOfWork ) return new List(); var result = await unitOfWork.Database.GetAppliedMigrationsAsync(); return result.ToList(); @@ -23,7 +23,7 @@ public static async Task> GetAppliedMigrationsAsync( this IUnitOfWo /// 取消令牌 public static async Task MigrateAsync( this IUnitOfWork source, CancellationToken cancellationToken = default ) { source.CheckNull( nameof( source ) ); - if ( source is not UnitOfWorkBase unitOfWork ) + if ( source is not DbContext unitOfWork ) return; await unitOfWork.Database.MigrateAsync( cancellationToken ); } @@ -34,7 +34,7 @@ public static async Task> GetAppliedMigrationsAsync( this IUnitOfWo /// 工作单元 public static bool EnsureCreated( this IUnitOfWork source ) { source.CheckNull( nameof( source ) ); - if( source is not UnitOfWorkBase unitOfWork ) + if( source is not DbContext unitOfWork ) return false; return unitOfWork.Database.EnsureCreated(); } @@ -45,7 +45,7 @@ public static bool EnsureCreated( this IUnitOfWork source ) { /// 工作单元 public static async Task EnsureCreatedAsync( this IUnitOfWork source ) { source.CheckNull( nameof( source ) ); - if( source is not UnitOfWorkBase unitOfWork ) + if( source is not DbContext unitOfWork ) return false; return await unitOfWork.Database.EnsureCreatedAsync(); } @@ -56,7 +56,7 @@ public static async Task EnsureCreatedAsync( this IUnitOfWork source ) { /// 工作单元 public static bool EnsureDeleted( this IUnitOfWork source ) { source.CheckNull( nameof( source ) ); - if( source is not UnitOfWorkBase unitOfWork ) + if( source is not DbContext unitOfWork ) return false; return unitOfWork.Database.EnsureDeleted(); } @@ -67,7 +67,7 @@ public static bool EnsureDeleted( this IUnitOfWork source ) { /// 工作单元 public static async Task EnsureDeletedAsync( this IUnitOfWork source ) { source.CheckNull( nameof( source ) ); - if( source is not UnitOfWorkBase unitOfWork ) + if( source is not DbContext unitOfWork ) return false; return await unitOfWork.Database.EnsureDeletedAsync(); } @@ -78,7 +78,7 @@ public static async Task EnsureDeletedAsync( this IUnitOfWork source ) { /// 工作单元 public static void ClearCache( this IUnitOfWork source ) { source.CheckNull( nameof( source ) ); - if ( source is not UnitOfWorkBase unitOfWork ) + if ( source is not DbContext unitOfWork ) return; unitOfWork.ChangeTracker.Clear(); } @@ -89,7 +89,7 @@ public static void ClearCache( this IUnitOfWork source ) { /// 工作单元 public static string GetChangeTrackerDebugView( this IUnitOfWork source ) { source.CheckNull( nameof( source ) ); - if( source is not UnitOfWorkBase unitOfWork ) + if( source is not DbContext unitOfWork ) return null; unitOfWork.ChangeTracker.DetectChanges(); return unitOfWork.ChangeTracker.DebugView.LongView; @@ -101,8 +101,20 @@ public static string GetChangeTrackerDebugView( this IUnitOfWork source ) { /// 工作单元 public static bool IsChange( this IUnitOfWork source ) { source.CheckNull( nameof( source ) ); - if ( source is not UnitOfWorkBase unitOfWork ) + if ( source is not DbContext unitOfWork ) return false; return unitOfWork.ChangeTracker.Entries().Any( t => t.State == EntityState.Modified ); } + + /// + /// 是否可连接 + /// + /// 工作单元 + /// 取消令牌 + public static async Task CanConnectAsync( this IUnitOfWork source, CancellationToken cancellationToken = default ) { + source.CheckNull( nameof( source ) ); + if ( source is not DbContext unitOfWork ) + return false; + return await unitOfWork.Database.CanConnectAsync( cancellationToken ); + } } \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Usings.cs b/src/Util.Data.EntityFrameworkCore/Usings.cs index 8bbcd04d1..bdf0a4378 100644 --- a/src/Util.Data.EntityFrameworkCore/Usings.cs +++ b/src/Util.Data.EntityFrameworkCore/Usings.cs @@ -3,36 +3,19 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; global using System.Text; -global using System.IO; -global using System.Data; -global using System.Linq.Dynamic.Core; global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; global using System.Text.Json; global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; global using System.Text.Encodings.Web; global using System.Text.Json.Serialization; global using System.Text.Unicode; -global using System.Data.Common; -global using System.Buffers; -global using System.Collections; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; global using System.ComponentModel.DataAnnotations.Schema; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.ChangeTracking; global using Microsoft.EntityFrameworkCore.Storage.ValueConversion; global using Microsoft.EntityFrameworkCore.Metadata; -global using Microsoft.EntityFrameworkCore.Infrastructure; \ No newline at end of file diff --git a/src/Util.Data.Metadata/Usings.cs b/src/Util.Data.Metadata/Usings.cs index c6554431f..e8a5e3a49 100644 --- a/src/Util.Data.Metadata/Usings.cs +++ b/src/Util.Data.Metadata/Usings.cs @@ -1,31 +1,3 @@ -global using System; -global using System.Threading.Tasks; +global using System.Threading.Tasks; global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Collections; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.Data.Sql/Usings.cs b/src/Util.Data.Sql/Usings.cs index c6554431f..c1dab6d39 100644 --- a/src/Util.Data.Sql/Usings.cs +++ b/src/Util.Data.Sql/Usings.cs @@ -2,30 +2,7 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; global using System.Text; -global using System.IO; global using System.Data; -global using System.Linq.Dynamic.Core; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; global using System.Collections; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.Domain.Biz/Usings.cs b/src/Util.Domain.Biz/Usings.cs index 8f2f1d4fa..5558ed8ff 100644 --- a/src/Util.Domain.Biz/Usings.cs +++ b/src/Util.Domain.Biz/Usings.cs @@ -1,28 +1 @@ -global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file +global using System.ComponentModel; diff --git a/src/Util.Domain/Usings.cs b/src/Util.Domain/Usings.cs index 8f2f1d4fa..39f0dc68b 100644 --- a/src/Util.Domain/Usings.cs +++ b/src/Util.Domain/Usings.cs @@ -3,26 +3,7 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; global using System.Text; -global using System.IO; global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file +global using System.ComponentModel.DataAnnotations; \ No newline at end of file diff --git a/src/Util.Events.Abstractions/Usings.cs b/src/Util.Events.Abstractions/Usings.cs index 62ad476d2..74f57adbd 100644 --- a/src/Util.Events.Abstractions/Usings.cs +++ b/src/Util.Events.Abstractions/Usings.cs @@ -1,8 +1,3 @@ global using System; global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file +global using System.Threading; \ No newline at end of file diff --git a/src/Util.Events/Infrastructure/LocalEventBusServiceRegistrar.cs b/src/Util.Events/Infrastructure/LocalEventBusServiceRegistrar.cs index 4f3f3a418..fca0582a0 100644 --- a/src/Util.Events/Infrastructure/LocalEventBusServiceRegistrar.cs +++ b/src/Util.Events/Infrastructure/LocalEventBusServiceRegistrar.cs @@ -38,8 +38,8 @@ public Action Register( ServiceContext serviceContext ) { /// 注册依赖 /// private void RegisterDependency( IServiceCollection services ) { - services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddTransient(); + services.TryAddTransient(); } /// diff --git a/src/Util.Events/Usings.cs b/src/Util.Events/Usings.cs index 62ad476d2..e92039a8a 100644 --- a/src/Util.Events/Usings.cs +++ b/src/Util.Events/Usings.cs @@ -3,6 +3,5 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.FileStorage.Minio/Usings.cs b/src/Util.FileStorage.Minio/Usings.cs index 001ad83c2..f1afe42f1 100644 --- a/src/Util.FileStorage.Minio/Usings.cs +++ b/src/Util.FileStorage.Minio/Usings.cs @@ -3,29 +3,9 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; global using System.Net.Http; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Minio; global using Minio.Exceptions; \ No newline at end of file diff --git a/src/Util.FileStorage/Usings.cs b/src/Util.FileStorage/Usings.cs index 8f2f1d4fa..51e7d135d 100644 --- a/src/Util.FileStorage/Usings.cs +++ b/src/Util.FileStorage/Usings.cs @@ -1,28 +1,5 @@ global using System; global using System.Threading.Tasks; global using System.Collections.Generic; -global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.Generators.Razor/Usings.cs b/src/Util.Generators.Razor/Usings.cs index 2dce8b6f5..6db9905e7 100644 --- a/src/Util.Generators.Razor/Usings.cs +++ b/src/Util.Generators.Razor/Usings.cs @@ -1,29 +1,5 @@ global using System; global using System.Threading.Tasks; global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.Data; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.Generators/Usings.cs b/src/Util.Generators/Usings.cs index 92b8c3c09..97692f7e9 100644 --- a/src/Util.Generators/Usings.cs +++ b/src/Util.Generators/Usings.cs @@ -2,29 +2,8 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; global using System.Diagnostics; -global using System.Text; global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; global using System.Data; global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Humanizer; diff --git a/src/Util.Images.Avatar/Usings.cs b/src/Util.Images.Avatar/Usings.cs index e28169e43..759c7debf 100644 --- a/src/Util.Images.Avatar/Usings.cs +++ b/src/Util.Images.Avatar/Usings.cs @@ -1,39 +1,5 @@ global using System; global using System.Threading.Tasks; -global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using System.Numerics; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; -global using SixLabors.ImageSharp; global using SixLabors.Fonts; -global using SixLabors.ImageSharp.Drawing.Processing; -global using SixLabors.ImageSharp.Processing; -global using SixLabors.ImageSharp.PixelFormats; -global using SixLabors.ImageSharp.Formats; -global using SixLabors.ImageSharp.Formats.Bmp; -global using SixLabors.ImageSharp.Formats.Gif; -global using SixLabors.ImageSharp.Formats.Jpeg; -global using SixLabors.ImageSharp.Formats.Png; \ No newline at end of file diff --git a/src/Util.Images.ImageSharp/Usings.cs b/src/Util.Images.ImageSharp/Usings.cs index e28169e43..c1e13363e 100644 --- a/src/Util.Images.ImageSharp/Usings.cs +++ b/src/Util.Images.ImageSharp/Usings.cs @@ -1,32 +1,10 @@ -global using System; -global using System.Threading.Tasks; +global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using System.Collections.Concurrent; global using System.Numerics; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using SixLabors.ImageSharp; global using SixLabors.Fonts; global using SixLabors.ImageSharp.Drawing.Processing; diff --git a/src/Util.Localization/01-Util.Localization.csproj b/src/Util.Localization/01-Util.Localization.csproj index f0d73486e..53cb902e6 100644 --- a/src/Util.Localization/01-Util.Localization.csproj +++ b/src/Util.Localization/01-Util.Localization.csproj @@ -5,7 +5,7 @@ icon.jpg Util.Localization Util.Localization - Util.Localization是Util应用框架的本地化封装类库 + Util.Localization是Util应用框架的Json本地化操作类库 @@ -27,7 +27,11 @@ - + + + + + diff --git a/src/Util.Localization/AppBuilderExtensions.cs b/src/Util.Localization/AppBuilderExtensions.cs index 3b8a219f3..b6b909882 100644 --- a/src/Util.Localization/AppBuilderExtensions.cs +++ b/src/Util.Localization/AppBuilderExtensions.cs @@ -34,4 +34,33 @@ public static IAppBuilder AddJsonLocalization( this IAppBuilder builder, string } ); return builder; } + + /// + /// 配置Json本地化 + /// + /// 应用生成器 + /// Json本地化配置操作 + public static IAppBuilder AddJsonLocalization( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + var options = new JsonLocalizationOptions(); + setupAction?.Invoke( options ); + builder.AddJsonLocalization( options.ResourcesPath ); + if( options.Cultures == null || options.Cultures.Count == 0 ) + return builder; + builder.Host.ConfigureServices( ( _, services ) => { + services.Configure( localizationOptions => { + var supportedCultures = options.Cultures.Select( culture => new CultureInfo( culture ) ).ToList(); + localizationOptions.DefaultRequestCulture = new RequestCulture( culture: supportedCultures[0], uiCulture: supportedCultures[0] ); + localizationOptions.SupportedCultures = supportedCultures; + localizationOptions.SupportedUICultures = supportedCultures; + localizationOptions.AddInitialRequestCultureProvider( new CustomRequestCultureProvider( async context => { + var culture = context.Request.Headers.ContentLanguage.FirstOrDefault(); + if ( culture.IsEmpty() ) + culture = "zh-CN"; + return await Task.FromResult( new ProviderCultureResult( culture ) ); + } ) ); + } ); + } ); + return builder; + } } \ No newline at end of file diff --git a/src/Util.Localization/JsonLocalizationOptions.cs b/src/Util.Localization/JsonLocalizationOptions.cs new file mode 100644 index 000000000..ae29cfe02 --- /dev/null +++ b/src/Util.Localization/JsonLocalizationOptions.cs @@ -0,0 +1,24 @@ +namespace Util.Localization; + +/// +/// Json本地化配置 +/// +public class JsonLocalizationOptions { + /// + /// 初始化Json本地化配置 + /// + public JsonLocalizationOptions() { + ResourcesPath = "Resources"; + Cultures = new List(); + } + + /// + /// 资源路径 + /// + public string ResourcesPath { get; set; } + + /// + /// 语言文化 + /// + public IList Cultures { get; set; } +} \ No newline at end of file diff --git a/src/Util.Localization/Usings.cs b/src/Util.Localization/Usings.cs index e3fc17cc4..cb32b443a 100644 --- a/src/Util.Localization/Usings.cs +++ b/src/Util.Localization/Usings.cs @@ -1,29 +1,15 @@ global using System; -global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; global using System.IO; global using System.Reflection; -global using System.ComponentModel; global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; +global using System.Threading.Tasks; global using System.Collections.Concurrent; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Localization; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.Extensions.Localization; \ No newline at end of file diff --git a/src/Util.Logging.Serilog.Exceptionless/Usings.cs b/src/Util.Logging.Serilog.Exceptionless/Usings.cs index 21bd97608..f5414d916 100644 --- a/src/Util.Logging.Serilog.Exceptionless/Usings.cs +++ b/src/Util.Logging.Serilog.Exceptionless/Usings.cs @@ -1,32 +1,6 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Serilog; -global using Serilog.Core; -global using Serilog.Events; -global using Serilog.Configuration; global using Exceptionless; \ No newline at end of file diff --git a/src/Util.Logging.Serilog/AppBuilderExtensions.cs b/src/Util.Logging.Serilog/AppBuilderExtensions.cs index f1df39afc..b416cbf3d 100644 --- a/src/Util.Logging.Serilog/AppBuilderExtensions.cs +++ b/src/Util.Logging.Serilog/AppBuilderExtensions.cs @@ -21,16 +21,41 @@ public static IAppBuilder AddSerilog( this IAppBuilder builder ) { /// 应用生成器 /// 是否清除默认设置的日志提供程序 public static IAppBuilder AddSerilog( this IAppBuilder builder, bool isClearProviders ) { + return builder.AddSerilog( options => { + options.IsClearProviders = isClearProviders; + } ); + } + + /// + /// 配置Serilog日志操作 + /// + /// 应用生成器 + /// 应用程序名称 + public static IAppBuilder AddSerilog( this IAppBuilder builder, string appName ) { + return builder.AddSerilog( options => { + options.Application = appName; + } ); + } + + /// + /// 配置Serilog日志操作 + /// + /// 应用生成器 + /// 日志配置操作 + public static IAppBuilder AddSerilog( this IAppBuilder builder, Action setupAction ) { builder.CheckNull( nameof( builder ) ); + var options = new LogOptions(); + setupAction?.Invoke( options ); builder.Host.ConfigureServices( ( context, services ) => { services.AddSingleton(); services.AddTransient( typeof( ILog<> ), typeof( Log<> ) ); services.AddTransient( typeof( ILog ), t => t.GetService()?.CreateLog( "default" ) ?? NullLog.Instance ); var configuration = context.Configuration; services.AddLogging( loggingBuilder => { - if ( isClearProviders ) + if ( options.IsClearProviders ) loggingBuilder.ClearProviders(); SerilogLog.Logger = new LoggerConfiguration() + .Enrich.WithProperty( "Application", options.Application ) .Enrich.FromLogContext() .Enrich.WithLogLevel() .Enrich.WithLogContext() diff --git a/src/Util.Logging.Serilog/Enrichers/LogContextEnricher.cs b/src/Util.Logging.Serilog/Enrichers/LogContextEnricher.cs index f338447e9..03882eb1f 100644 --- a/src/Util.Logging.Serilog/Enrichers/LogContextEnricher.cs +++ b/src/Util.Logging.Serilog/Enrichers/LogContextEnricher.cs @@ -6,11 +6,6 @@ namespace Util.Logging.Serilog.Enrichers; /// 日志上下文扩展属性 /// public class LogContextEnricher : ILogEventEnricher { - /// - /// 日志上下文 - /// - private LogContext _context; - /// /// 扩展属性 /// @@ -20,16 +15,20 @@ public void Enrich( LogEvent logEvent, ILogEventPropertyFactory propertyFactory var accessor = Ioc.Create(); if ( accessor == null ) return; - _context = accessor.Context; - if ( _context == null ) + var context = accessor.Context; + if ( context == null ) + return; + if ( logEvent == null ) + return; + if ( propertyFactory == null ) return; RemoveProperties( logEvent ); - AddDuration( logEvent, propertyFactory ); - AddTraceId( logEvent, propertyFactory ); - AddUserId( logEvent, propertyFactory ); - AddApplication( logEvent, propertyFactory ); - AddEnvironment( logEvent, propertyFactory ); - AddData( logEvent, propertyFactory ); + AddDuration( context,logEvent, propertyFactory ); + AddTraceId( context, logEvent, propertyFactory ); + AddUserId( context, logEvent, propertyFactory ); + AddApplication( context, logEvent, propertyFactory ); + AddEnvironment( context, logEvent, propertyFactory ); + AddData( context, logEvent, propertyFactory ); } /// @@ -46,60 +45,60 @@ private void RemoveProperties( LogEvent logEvent ) { /// /// 添加执行持续时间 /// - private void AddDuration( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { - if ( _context.Stopwatch == null ) + private void AddDuration( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context?.Stopwatch == null ) return; - var property = propertyFactory.CreateProperty( "Duration", _context.Stopwatch.Elapsed.Description() ); + var property = propertyFactory.CreateProperty( "Duration", context.Stopwatch.Elapsed.Description() ); logEvent.AddOrUpdateProperty( property ); } /// /// 添加跟踪号 /// - private void AddTraceId( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { - if ( _context.TraceId.IsEmpty() ) + private void AddTraceId( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context == null || context.TraceId.IsEmpty() ) return; - var property = propertyFactory.CreateProperty( "TraceId", _context.TraceId ); + var property = propertyFactory.CreateProperty( "TraceId", context.TraceId ); logEvent.AddOrUpdateProperty( property ); } /// /// 添加用户标识 /// - private void AddUserId( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { - if ( _context.UserId.IsEmpty() ) + private void AddUserId( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context == null || context.UserId.IsEmpty() ) return; - var property = propertyFactory.CreateProperty( "UserId", _context.UserId ); + var property = propertyFactory.CreateProperty( "UserId", context.UserId ); logEvent.AddOrUpdateProperty( property ); } /// /// 添加应用程序 /// - private void AddApplication( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { - if ( _context.Application.IsEmpty() ) + private void AddApplication( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context == null || context.Application.IsEmpty() ) return; - var property = propertyFactory.CreateProperty( "Application", _context.Application ); + var property = propertyFactory.CreateProperty( "Application", context.Application ); logEvent.AddOrUpdateProperty( property ); } /// /// 添加执行环境 /// - private void AddEnvironment( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { - if ( _context.Environment.IsEmpty() ) + private void AddEnvironment( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context == null || context.Environment.IsEmpty() ) return; - var property = propertyFactory.CreateProperty( "Environment", _context.Environment ); + var property = propertyFactory.CreateProperty( "Environment", context.Environment ); logEvent.AddOrUpdateProperty( property ); } /// /// 添加扩展数据 /// - private void AddData( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { - if ( _context.Data.Count == 0 ) + private void AddData( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context?.Data == null || context.Data.Count == 0 ) return; - foreach ( var item in _context.Data ) { + foreach ( var item in context.Data ) { var property = propertyFactory.CreateProperty( item.Key, item.Value ); logEvent.AddOrUpdateProperty( property ); } diff --git a/src/Util.Logging.Serilog/LogOptions.cs b/src/Util.Logging.Serilog/LogOptions.cs new file mode 100644 index 000000000..b06e1e80b --- /dev/null +++ b/src/Util.Logging.Serilog/LogOptions.cs @@ -0,0 +1,15 @@ +namespace Util.Logging.Serilog; + +/// +/// 日志配置 +/// +public class LogOptions { + /// + /// 是否清除默认设置的日志提供程序 + /// + public bool IsClearProviders { get; set; } + /// + /// 应用程序名称 + /// + public string Application { get; set; } +} \ No newline at end of file diff --git a/src/Util.Logging.Serilog/Usings.cs b/src/Util.Logging.Serilog/Usings.cs index 8a7aee74f..e3d303199 100644 --- a/src/Util.Logging.Serilog/Usings.cs +++ b/src/Util.Logging.Serilog/Usings.cs @@ -1,30 +1,7 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Serilog; global using Serilog.Core; global using Serilog.Events; diff --git a/src/Util.Logging/Usings.cs b/src/Util.Logging/Usings.cs index d95c3e216..cf49b8375 100644 --- a/src/Util.Logging/Usings.cs +++ b/src/Util.Logging/Usings.cs @@ -1,27 +1,5 @@ global using System; -global using System.Threading.Tasks; global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; global using System.Diagnostics; global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/03-Util.Microservices.Dapr.csproj b/src/Util.Microservices.Dapr/03-Util.Microservices.Dapr.csproj new file mode 100644 index 000000000..6ce924357 --- /dev/null +++ b/src/Util.Microservices.Dapr/03-Util.Microservices.Dapr.csproj @@ -0,0 +1,40 @@ + + + $(NetTargetFramework) + icon.jpg + Util.Microservices.Dapr + Util.Microservices.Dapr + Util.Microservices.Dapr是Util应用框架基于Dapr的微服务操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Microservices.Dapr.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Microservices.Dapr.xml + + + + + True + False + + + + + + + + + + + + + + + + + diff --git a/src/Util.Microservices.Dapr/AppBuilderExtensions.cs b/src/Util.Microservices.Dapr/AppBuilderExtensions.cs new file mode 100644 index 000000000..c36f10d28 --- /dev/null +++ b/src/Util.Microservices.Dapr/AppBuilderExtensions.cs @@ -0,0 +1,96 @@ +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Unicode; +using Util.Configs; +using Util.Helpers; +using Util.SystemTextJson; + +namespace Util.Microservices.Dapr; + +/// +/// Dapr微服务操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Dapr微服务操作 + /// + /// 应用生成器 + public static IAppBuilder AddDapr( this IAppBuilder builder ) { + return builder.AddDapr( null ); + } + + /// + /// 配置Dapr微服务操作 + /// + /// 应用生成器 + /// Dapr配置操作 + public static IAppBuilder AddDapr( this IAppBuilder builder, Action setupAction ) { + return builder.AddDapr( setupAction, null ); + } + + /// + /// 配置Dapr微服务操作 + /// + /// 应用生成器 + /// Dapr配置操作 + /// Dapr客户端生成操作 + public static IAppBuilder AddDapr( this IAppBuilder builder, Action setupAction, Action buildAction ) { + var options = new DaprOptions(); + setupAction?.Invoke( options ); + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + services.AddDaprClient( clientBuilder => { + clientBuilder.UseJsonSerializationOptions( GetJsonSerializerOptions() ); + buildAction?.Invoke( clientBuilder ); + } ); + if( setupAction != null ) + services.Configure( setupAction ); + } ); + builder.Host.ConfigureAppConfiguration( ( context, configurationBuilder ) => { + AddDaprSecretStore( configurationBuilder, options, buildAction ); + } ); + return builder; + } + + /// + /// 获取Json序列化配置 + /// + private static JsonSerializerOptions GetJsonSerializerOptions() { + return new JsonSerializerOptions { + PropertyNameCaseInsensitive = true, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter() + } + }; + } + + /// + /// 配置Dapr机密存储器 + /// + private static void AddDaprSecretStore( IConfigurationBuilder configurationBuilder , DaprOptions options, Action buildAction ) { + if ( options.SecretStoreName.IsEmpty() ) + return; + try { + configurationBuilder.AddDaprSecretStore( options.SecretStoreName, CreateDaprClient( buildAction ) ); + } + catch ( Exception ) { + if ( Common.IsLinux ) + throw; + } + } + + /// + /// 创建Dapr客户端 + /// + private static DaprClient CreateDaprClient( Action buildAction ) { + var clientBuilder = new DaprClientBuilder(); + clientBuilder.UseJsonSerializationOptions( GetJsonSerializerOptions() ); + buildAction?.Invoke( clientBuilder ); + return clientBuilder.Build(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/CommandLines/DaprRunCommand.cs b/src/Util.Microservices.Dapr/CommandLines/DaprRunCommand.cs new file mode 100644 index 000000000..53946d616 --- /dev/null +++ b/src/Util.Microservices.Dapr/CommandLines/DaprRunCommand.cs @@ -0,0 +1,269 @@ +using System.Net.Sockets; +using System.Net; +using Util.Helpers; + +namespace Util.Microservices.Dapr.CommandLines; + +/// +/// dapr run 命令 +/// +public class DaprRunCommand { + /// + /// 日志操作 + /// + private ILogger _logger; + /// + /// 应用标识 + /// + private string _appId; + /// + /// 应用端口 + /// + private int _appPort; + /// + /// 应用访问协议 + /// + private string _appProtocol; + /// + /// Dapr Http端口 + /// + private int _daprHttpPort; + /// + /// Dapr Grpc端口 + /// + private int _daprGrpcPort; + /// + /// 度量端口 + /// + private int _metricsPort; + /// + /// Dapr组件配置目录路径 + /// + private string _componentsPath; + /// + /// Dapr配置目录路径 + /// + private string _configPath; + /// + /// dotnet项目文件路径 + /// + private string _project; + /// + /// 是否使用空闲端口 + /// + private bool _isUseFreePorts; + + /// + /// 封闭构造方法 + /// + private DaprRunCommand() { + _logger = NullLogger.Instance; + } + + /// + /// 创建dapr run命令 + /// + /// 应用标识 + public static DaprRunCommand Create( string appId ) { + return new DaprRunCommand().AppId( appId ); + } + + /// + /// 获取应用协议 + /// + public string GetAppProtocol() { + return _appProtocol; + } + + /// + /// 获取应用端口 + /// + public int GetAppPort() { + return _appPort; + } + + /// + /// 获取Dapr Http端口 + /// + public int GetDaprHttpPort() { + return _daprHttpPort; + } + + /// + /// 获取Dapr Grpc端口 + /// + public int GetDaprGrpcPort() { + return _daprGrpcPort; + } + + /// + /// 获取度量端口 + /// + public int GetMetricsPort() { + return _metricsPort; + } + + /// + /// 设置应用标识 + /// + /// 应用标识 + private DaprRunCommand AppId( string id ) { + _appId = id; + return this; + } + + /// + /// 设置日志操作 + /// + public DaprRunCommand Log( ILogger logger ) { + _logger = logger; + return this; + } + + /// + /// 设置应用端口,如果未设置则自动获取 + /// + /// 应用Http端口 + public DaprRunCommand AppPort( int port ) { + _appPort = port; + return this; + } + + /// + /// 设置Dapr访问应用协议 + /// + /// 访问协议,可选值为: Http,Grpc,默认值为: Http + public DaprRunCommand AppProtocol( string protocol ) { + _appProtocol = protocol; + return this; + } + + /// + /// 设置Dapr Http端口,如果未设置则自动获取 + /// + /// Dapr Http端口 + public DaprRunCommand DaprHttpPort( int port ) { + _daprHttpPort = port; + return this; + } + + /// + /// 设置Dapr Grpc端口,如果未设置则自动获取 + /// + /// Dapr Grpc端口 + public DaprRunCommand DaprGrpcPort( int port ) { + _daprGrpcPort = port; + return this; + } + + /// + /// 设置度量端口,如果未设置则自动获取 + /// + /// 度量端口 + public DaprRunCommand MetricsPort( int port ) { + _metricsPort = port; + return this; + } + + /// + /// 设置Dapr组件配置目录路径 + /// + /// Dapr组件配置目录路径 + public DaprRunCommand ComponentsPath( string path ) { + _componentsPath = path; + return this; + } + + /// + /// 设置Dapr配置目录路径 + /// + /// Dapr配置目录路径 + public DaprRunCommand ConfigPath( string path ) { + _configPath = path; + return this; + } + + /// + /// 设置dotnet项目文件路径 + /// + /// dotnet项目文件路径,范例: ../../test/app.csproj + public DaprRunCommand Project( string project ) { + _project = project; + return this; + } + + /// + /// 设置或取消使用空闲端口 + /// + /// 是否使用空闲端口 + public DaprRunCommand UseFreePorts( bool isUseFreePorts = true ) { + _isUseFreePorts = isUseFreePorts; + return this; + } + + /// + /// 执行命令 + /// + public void Execute() { + CreateCommandLine() + .OutputToMatch( "dapr initialized. Status: Running." ) + .Execute(); + } + + /// + /// 创建命令行操作 + /// + private CommandLine CreateCommandLine() { + if ( _isUseFreePorts ) + GetFreePorts(); + return CommandLine.Create( "dapr" ) + .Log( _logger ) + .Arguments( "run" ) + .Arguments( "--app-id", _appId ) + .ArgumentsIf( _appPort != 0, "--app-port", _appPort.ToString( CultureInfo.InvariantCulture ) ) + .ArgumentsIf( _appProtocol.IsEmpty() == false, "--app-protocol", _appProtocol ) + .ArgumentsIf( _daprHttpPort != 0, "--dapr-http-port", _daprHttpPort.ToString( CultureInfo.InvariantCulture ) ) + .ArgumentsIf( _daprGrpcPort != 0, "--dapr-grpc-port", _daprGrpcPort.ToString( CultureInfo.InvariantCulture ) ) + .ArgumentsIf( _metricsPort != 0, "--metrics-port", _metricsPort.ToString( CultureInfo.InvariantCulture ) ) + .ArgumentsIf( _componentsPath.IsEmpty() == false, "--resources-path", _componentsPath ) + .ArgumentsIf( _configPath.IsEmpty() == false, "--config", _configPath ) + .Arguments( "--log-level", "debug" ) + .Arguments( "--" ) + .Arguments( "dotnet", "run" ) + .ArgumentsIf( _project.IsEmpty() == false, "--project", _project ) + .ArgumentsIf( _appPort != 0, "--urls", $"http://localhost:{_appPort.ToString( CultureInfo.InvariantCulture )}" ); + } + + /// + /// 获取空闲端口 + /// + private void GetFreePorts() { + const int NUM_LISTENERS = 4; + IPAddress ip = IPAddress.Loopback; + var listeners = new TcpListener[NUM_LISTENERS]; + var ports = new int[NUM_LISTENERS]; + for ( int i = 0; i < NUM_LISTENERS; i++ ) { + listeners[i] = new TcpListener( ip, 0 ); + listeners[i].Start(); + ports[i] = ( (IPEndPoint)listeners[i].LocalEndpoint ).Port; + } + for ( int i = 0; i < NUM_LISTENERS; i++ ) { + listeners[i].Stop(); + } + if ( _appPort == 0 ) + _appPort = ports[0]; + if ( _daprHttpPort == 0 ) + _daprHttpPort = ports[1]; + if ( _daprGrpcPort == 0 ) + _daprGrpcPort = ports[2]; + if ( _metricsPort == 0 ) + _metricsPort = ports[3]; + } + + /// + /// 获取命令调试文本 + /// + public string GetDebugText() { + return CreateCommandLine().GetDebugText(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/CommandLines/DaprStopCommand.cs b/src/Util.Microservices.Dapr/CommandLines/DaprStopCommand.cs new file mode 100644 index 000000000..28e016362 --- /dev/null +++ b/src/Util.Microservices.Dapr/CommandLines/DaprStopCommand.cs @@ -0,0 +1,76 @@ +using Util.Helpers; + +namespace Util.Microservices.Dapr.CommandLines; + +/// +/// dapr stop命令 +/// +public class DaprStopCommand { + /// + /// 日志操作 + /// + private ILogger _logger; + /// + /// 应用标识 + /// + private string _appId; + + /// + /// 封闭构造方法 + /// + private DaprStopCommand() { + _logger = NullLogger.Instance; + } + + /// + /// 创建dapr stop命令 + /// + /// 应用标识 + public static DaprStopCommand Create( string appId ) { + return new DaprStopCommand().AppId( appId ); + } + + /// + /// 设置应用标识 + /// + /// 应用标识 + private DaprStopCommand AppId( string id ) { + _appId = id; + return this; + } + + /// + /// 设置日志操作 + /// + public DaprStopCommand Log( ILogger logger ) { + _logger = logger; + return this; + } + + /// + /// 执行命令 + /// + public void Execute() { + CreateCommandLine() + .OutputToMatch( "app stopped successfully" ) + .OutputToMatch( "failed to stop app id" ) + .Execute(); + } + + /// + /// 创建命令行操作 + /// + private CommandLine CreateCommandLine() { + return CommandLine.Create( "dapr" ) + .Log( _logger ) + .Arguments( "stop" ) + .Arguments( "--app-id", _appId ); + } + + /// + /// 获取命令调试文本 + /// + public string GetDebugText() { + return CreateCommandLine().GetDebugText(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/DaprEventBus.cs b/src/Util.Microservices.Dapr/DaprEventBus.cs new file mode 100644 index 000000000..d13f7f345 --- /dev/null +++ b/src/Util.Microservices.Dapr/DaprEventBus.cs @@ -0,0 +1,70 @@ +using Util.Data; +using Util.Events; + +namespace Util.Microservices.Dapr; + +/// +/// 基于Dapr的集成事件总线 +/// +public class DaprEventBus : IIntegrationEventBus { + /// + /// Dapr客户端 + /// + private readonly DaprClient _client; + /// + /// 日志操作 + /// + private readonly ILogger _logger; + /// + /// 工作单元操作管理器 + /// + private readonly IUnitOfWorkActionManager _actionManager; + + /// + /// 发布订阅配置名称,默认值: pubsub + /// + public static string PubsubName { get; set; } = "pubsub"; + + /// + /// 初始化Dapr集成事件总线 + /// + /// Dapr客户端 + /// 日志操作 + /// 工作单元操作管理器 + public DaprEventBus( DaprClient client, ILogger logger, IUnitOfWorkActionManager actionManager ) { + _client = client ?? throw new ArgumentNullException( nameof( client ) ); + _logger = logger ?? throw new ArgumentNullException( nameof( logger ) ); + _actionManager = actionManager ?? throw new ArgumentNullException( nameof( actionManager ) ); + } + + /// + /// 发布事件 + /// + /// 事件类型 + /// 事件 + /// 是否立即发送事件,默认为true,如果希望发送操作延迟到工作单元提交成功后,则设置为false + /// 主题名称,默认使用类型名称作为主题 + /// 发布订阅配置名称,默认值: pubsub + /// 取消令牌 + public async Task PublishAsync( TEvent @event, bool sendNow = true, string topic = "", string pubsubName = "", CancellationToken cancellationToken = default ) + where TEvent : IIntegrationEvent { + @event.CheckNull( nameof( @event ) ); + if ( sendNow ) { + await PublishEventAsync( @event, topic, pubsubName, cancellationToken ); + return; + } + _actionManager.Register( async () => { + await PublishEventAsync( @event, topic, pubsubName, cancellationToken ); + } ); + } + + /// + /// 发布事件 + /// + private async Task PublishEventAsync( TEvent @event, string topic, string pubsubName, CancellationToken cancellationToken ) { + pubsubName = pubsubName.IsEmpty() ? PubsubName : pubsubName; + topic = topic.IsEmpty() ? @event.GetType().Name : topic; + _logger.LogInformation( "Publishing event {@Event} to {PubsubName}.{Topic}", @event, pubsubName, topic ); + await _client.PublishEventAsync( pubsubName, topic, (object)@event, cancellationToken ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/DaprMicroserviceClient.cs b/src/Util.Microservices.Dapr/DaprMicroserviceClient.cs new file mode 100644 index 000000000..ba7f0051a --- /dev/null +++ b/src/Util.Microservices.Dapr/DaprMicroserviceClient.cs @@ -0,0 +1,31 @@ +using Util.Microservices.Dapr.ServiceInvocations; + +namespace Util.Microservices.Dapr; + +/// +/// Dapr微服务客户端 +/// +public class DaprMicroserviceClient : IMicroserviceClient { + /// + /// 初始化Dapr微服务客户端 + /// + /// Dapr客户端 + /// Dapr Http客户端 + /// Dapr配置 + /// 应用标识 + /// 日志工厂 + public DaprMicroserviceClient( DaprClient client, HttpClient httpClient, IOptions options, string appId, ILoggerFactory loggerFactory ) { + HttpClient = new HttpClientService().SetHttpClient( httpClient ); + ServiceInvocation = new DaprServiceInvocation( client, options, loggerFactory ).Service( appId ); + } + + /// + /// Http客户端 + /// + public IHttpClient HttpClient { get; } + + /// + /// WebApi服务调用 + /// + public IServiceInvocation ServiceInvocation { get; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/DaprMicroserviceClientFactory.cs b/src/Util.Microservices.Dapr/DaprMicroserviceClientFactory.cs new file mode 100644 index 000000000..7d88e00d9 --- /dev/null +++ b/src/Util.Microservices.Dapr/DaprMicroserviceClientFactory.cs @@ -0,0 +1,93 @@ +namespace Util.Microservices.Dapr; + +/// +/// Dapr微服务客户端工厂 +/// +public class DaprMicroserviceClientFactory : IMicroserviceClientFactory { + /// + /// Dapr客户端 + /// + private readonly DaprClient _client; + /// + /// 应用标识 + /// + private string _appId; + /// + /// Dapr Http端口 + /// + private int _daprHttpPort; + /// + /// Dapr配置 + /// + private readonly IOptions _options; + /// + /// 日志工厂 + /// + private readonly ILoggerFactory _loggerFactory; + + + /// + /// 初始化Dapr微服务客户端生成器 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + public DaprMicroserviceClientFactory( DaprClient client = null, IOptions options = null, ILoggerFactory loggerFactory = null ) { + _client = client; + _options = options; + _loggerFactory = loggerFactory; + } + + /// + /// 设置应用标识 + /// + /// 应用标识 + public void AppId( string appId ) { + _appId = appId; + } + + /// + /// 设置Dapr Http端口 + /// + /// Dapr Http端口 + public void DaprHttpPort( int daprHttpPort ) { + _daprHttpPort = daprHttpPort; + } + + /// + public virtual IMicroserviceClient Create() { + var client = CreateDaprClient(); + var httpClient = CreateHttpClient(); + return new DaprMicroserviceClient( client, httpClient, _options, _appId, _loggerFactory ); + } + + /// + /// 创建Dapr客户端 + /// + protected virtual DaprClient CreateDaprClient() { + var httpEndpoint = GetDaprHttpEndpoint(); + if ( httpEndpoint.IsEmpty() == false ) { + return new DaprClientBuilder() + .UseHttpEndpoint( httpEndpoint ) + .Build(); + } + return _client ?? new DaprClientBuilder().Build(); + } + + /// + /// 获取Dapr Http端点 + /// + protected virtual string GetDaprHttpEndpoint() { + return _daprHttpPort > 0 ? $"http://localhost:{_daprHttpPort}" : null; + } + + /// + /// 创建Dapr Http客户端 + /// + protected virtual HttpClient CreateHttpClient() { + if ( _appId.IsEmpty() ) + return DaprClient.CreateInvokeHttpClient(); + var endpoint = GetDaprHttpEndpoint(); + return endpoint.IsEmpty() ? DaprClient.CreateInvokeHttpClient( _appId ) : DaprClient.CreateInvokeHttpClient( _appId, endpoint ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/DaprOptions.cs b/src/Util.Microservices.Dapr/DaprOptions.cs new file mode 100644 index 000000000..23fef2152 --- /dev/null +++ b/src/Util.Microservices.Dapr/DaprOptions.cs @@ -0,0 +1,22 @@ +namespace Util.Microservices.Dapr; + +/// +/// Dapr配置 +/// +public class DaprOptions { + /// + /// 初始化Dapr配置 + /// + public DaprOptions() { + ServiceInvocation = new ServiceInvocationOptions(); + } + + /// + /// 机密存储器名称 + /// + public string SecretStoreName { get; set; } + /// + /// 服务调用配置 + /// + public ServiceInvocationOptions ServiceInvocation { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/IMicroserviceClientFactoryExtensions.cs b/src/Util.Microservices.Dapr/IMicroserviceClientFactoryExtensions.cs new file mode 100644 index 000000000..77cbff955 --- /dev/null +++ b/src/Util.Microservices.Dapr/IMicroserviceClientFactoryExtensions.cs @@ -0,0 +1,28 @@ +namespace Util.Microservices.Dapr; + +/// +/// 微服务客户端工厂扩展 +/// +public static class IMicroserviceClientFactoryExtensions { + /// + /// 设置应用标识 + /// + /// 微服务客户端工厂 + /// 应用标识 + public static IMicroserviceClientFactory AppId( this IMicroserviceClientFactory source,string appId ) { + if ( source is DaprMicroserviceClientFactory factory ) + factory.AppId( appId ); + return source; + } + + /// + /// 设置Dapr Http端口 + /// + /// 微服务客户端工厂 + /// Dapr Http端口 + public static IMicroserviceClientFactory DaprHttpPort( this IMicroserviceClientFactory source, int daprHttpPort ) { + if ( source is DaprMicroserviceClientFactory factory ) + factory.DaprHttpPort( daprHttpPort ); + return source; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocationOptions.cs b/src/Util.Microservices.Dapr/ServiceInvocationOptions.cs new file mode 100644 index 000000000..d44af9694 --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocationOptions.cs @@ -0,0 +1,61 @@ +namespace Util.Microservices.Dapr; + +/// +/// 服务调用配置 +/// +public class ServiceInvocationOptions { + /// + /// 初始化服务调用配置 + /// + public ServiceInvocationOptions() { + RequestFilters = new List(); + ResponseFilters = new List(); + ImportHeaderKeys = new List { + "Authorization", + "x-correlation-id", + "Content-Language" + }; + IsUnpackResult = true; + } + + /// + /// 是否使用ServiceResult对结果解包 + /// + public bool IsUnpackResult { get; set; } + /// + /// 服务状态码转换事件,参数为业务状态码或Http状态码,取决于是否使用约定结果类型 + /// + public Func OnState { get; set; } + /// + /// 调用前事件,返回false取消调用 + /// + public Func OnBefore { get; set; } + /// + /// 调用成功事件 + /// + public Func OnSuccess { get; set; } + /// + /// 调用失败事件 + /// + public Func OnFail { get; set; } + /// + /// 调用未授权事件 + /// + public Func OnUnauthorized { get; set; } + /// + /// 调用完成事件 + /// + public Func OnComplete { get; set; } + /// + /// 请求过滤器集合 + /// + public IList RequestFilters { get; set; } + /// + /// 响应过滤器集合 + /// + public IList ResponseFilters { get; set; } + /// + /// 需要导入的请求头键名集合,从当前Http上下文导入 + /// + public IList ImportHeaderKeys { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocation.cs b/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocation.cs new file mode 100644 index 000000000..540b81577 --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocation.cs @@ -0,0 +1,555 @@ +using System.Net; + +namespace Util.Microservices.Dapr.ServiceInvocations; + +/// +/// Dapr WebApi服务调用操作 +/// +public class DaprServiceInvocation : DaprServiceInvocationBase, IServiceInvocation { + + #region 构造方法 + + /// + /// 初始化Dapr WebApi服务调用操作 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + public DaprServiceInvocation( DaprClient client, IOptions options, ILoggerFactory loggerFactory ) : base( client, options, loggerFactory ) { + } + + #endregion + + #region 事件 + + /// + /// 调用前事件 + /// + protected Func OnBeforeAction { get; set; } + /// + /// 响应结果转换事件 + /// + protected Func> OnResultAction { get; set; } + /// + /// 调用后事件 + /// + protected Action OnAfterAction { get; set; } + /// + /// 调用成功事件 + /// + protected Func OnSuccessAction { get; set; } + /// + /// 调用失败事件 + /// + protected Func OnFailAction { get; set; } + /// + /// 调用未授权事件 + /// + protected Func OnUnauthorizedAction { get; set; } + /// + /// 调用完成事件 + /// + protected Func OnCompleteAction { get; set; } + + /// + public IServiceInvocation OnBefore( Func action ) { + OnBeforeAction = action; + return this; + } + + /// + /// 响应结果转换事件 + /// + /// 响应结果转换操作 + public IServiceInvocation OnResult( Func> action ) { + OnResultAction = action; + return this; + } + + /// + /// 调用后事件 + /// + /// 调用后处理操作 + public IServiceInvocation OnAfter( Action action ) { + OnAfterAction = action; + return this; + } + + /// + public IServiceInvocation OnSuccess( Func action ) { + if ( action != null ) + OnSuccessAction = ( request, response, result ) => action( request, response, (TResponse)result ); + return this; + } + + /// + /// 调用失败事件 + /// + /// 调用失败处理操作 + public IServiceInvocation OnFail( Func action ) { + OnFailAction = action; + return this; + } + + /// + /// 调用未授权事件 + /// + /// 调用未授权处理操作 + public IServiceInvocation OnUnauthorized( Func action ) { + OnUnauthorizedAction = action; + return this; + } + + /// + /// 调用完成事件 + /// + /// 调用完成处理操作 + public IServiceInvocation OnComplete( Func action ) { + OnCompleteAction = action; + return this; + } + + #endregion + + #region 方法调用重载 + + /// + public async Task InvokeAsync( string methodName, CancellationToken cancellationToken = default ) { + await InvokeAsync( methodName, HttpMethod.Get, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + await InvokeAsync( methodName, null, httpMethod, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, object data, CancellationToken cancellationToken = default ) { + await InvokeAsync( methodName, data, HttpMethod.Post, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, object data, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + await InvokeAsync( methodName, data, httpMethod, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, HttpMethod.Get, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, null, httpMethod, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, object data, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, data, HttpMethod.Get, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, object data, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, data, httpMethod, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, TRequest data, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, data, HttpMethod.Get, cancellationToken ); + } + + #endregion + + #region InvokeAsync + + /// + public async Task InvokeAsync( string methodName, TRequest data, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + Log.LogTrace( "准备调用服务方法,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + var request = CreateRequest( methodName, data, httpMethod ); + request = FilterRequest( request ); + if ( InvokeBefore( request ) == false ) + return default; + HttpResponseMessage response = null; + try { + response = await Client.InvokeMethodWithResponseAsync( request, cancellationToken ); + response = FilterResponse( response ); + OnAfterAction?.Invoke( response ); + var result = await ToResult( response, cancellationToken ); + await InvokeAfter( methodName, request, response, result ); + return result.Data; + } + catch ( Warning ) { + throw; + } + catch ( Exception exception ) { + await FailHandlerAsync( methodName, request, response, exception, null ); + } + finally { + await CompleteHandlerAsync( methodName, request, response ); + } + return default; + } + + #endregion + + #region CreateRequest + + /// + /// 创建请求消息 + /// + protected virtual HttpRequestMessage CreateRequest( string methodName, TRequest data, HttpMethod httpMethod ) { + var result = CreateInvokeMethodRequest( methodName, data, httpMethod ); + AddHeaders( result, methodName ); + return result; + } + + /// + /// 创建请求消息 + /// + private HttpRequestMessage CreateInvokeMethodRequest( string methodName, TRequest data, HttpMethod httpMethod ) { + methodName = GetMethodName( methodName ); + methodName = GetMethodNameWithQueryString( methodName, data, httpMethod ); + if ( data == null || httpMethod == HttpMethod.Get ) + return Client.CreateInvokeMethodRequest( httpMethod, AppId, methodName ); + return Client.CreateInvokeMethodRequest( httpMethod, AppId, methodName, data ); + } + + /// + /// 获取方法名 + /// + /// 方法名 + protected virtual string GetMethodName( string methodName ) { + if ( methodName.IsEmpty() ) + return string.Empty; + if ( methodName.StartsWith( "/" ) ) + return methodName; + return $"/api/{methodName}"; + } + + /// + /// 获取带查询字符串的方法名 + /// + protected virtual string GetMethodNameWithQueryString( string methodName, TRequest data, HttpMethod httpMethod ) { + if ( httpMethod != HttpMethod.Get ) + return methodName; + if ( data == null ) + return methodName; + return QueryHelpers.AddQueryString( methodName, ToDictionary( data ) ); + } + + /// + /// 对象转换为字典 + /// + /// 对象 + protected IDictionary ToDictionary( object data ) { + var result = Util.Helpers.Convert.ToDictionary( data ); + return result.Where( t => t.Value != null ).ToDictionary( t => t.Key, t => t.Value.SafeString() ); + } + + /// + /// 添加请求头 + /// + protected virtual void AddHeaders( HttpRequestMessage message, string methodName ) { + RemoveHeaders(); + var headers = GetHeaders(); + foreach ( var key in headers.Keys ) { + bool? success = message.Headers.TryAddWithoutValidation( key, headers[key].ToArray() ); + if ( success.SafeValue() ) + continue; + message.Content ??= new StringContent( string.Empty ); + success = message.Content.Headers.TryAddWithoutValidation( key, headers[key].ToArray() ); + if ( success.SafeValue() ) + continue; + Log.LogWarning( "添加请求头失败,Key:{RequestHeaderKey},AppId:{AppId},MethodName:{MethodName}", key, AppId, methodName ); + } + } + + /// + /// 移除请求头 + /// + private void RemoveHeaders() { + foreach ( var key in RemoveHeaderKeys ) { + Headers.Remove( key ); + ImportHeaderKeys.Remove( key ); + Options.ServiceInvocation.ImportHeaderKeys.Remove( key ); + } + } + + /// + /// 获取请求头 + /// + private IDictionary GetHeaders() { + var result = GetImportHeaders(); + foreach ( var key in Headers.Keys ) { + if ( result.ContainsKey( key ) ) + result.Remove( key ); + result.Add( key, Headers[key] ); + } + return result; + } + + /// + /// 获取导入的请求头 + /// + private IDictionary GetImportHeaders() { + var result = new Dictionary(); + ImportHeaderKeys.AddRange( Options.ServiceInvocation.ImportHeaderKeys ); + if ( ImportHeaderKeys.Count == 0 ) + return result; + var headers = Util.Helpers.Web.Request?.Headers; + if ( headers == null ) + return result; + foreach ( var key in ImportHeaderKeys.Distinct() ) { + if ( headers.TryGetValue( key, out var value ) ) + result.Add( key, value ); + } + return result; + } + + #endregion + + #region InvokeBefore + + /// + /// 调用前操作 + /// + /// 请求消息 + protected bool InvokeBefore( HttpRequestMessage message ) { + if ( OnBeforeAction != null ) + return OnBeforeAction( message ); + if ( Options?.ServiceInvocation?.OnBefore == null ) + return true; + return Options.ServiceInvocation.OnBefore( message ); + } + + #endregion + + #region FilterRequest + + /// + /// 过滤请求 + /// + /// 请求消息 + protected virtual HttpRequestMessage FilterRequest( HttpRequestMessage request ) { + var context = new RequestContext( request, Util.Helpers.Web.HttpContext ); + var requestFilters = Options?.ServiceInvocation?.RequestFilters; + if ( requestFilters == null || requestFilters.Count == 0 ) + return request; + foreach ( var filter in requestFilters.Where( t => t is { Enabled: true } ).OrderBy( t => t.Order ) ) + filter.Handle( context ); + return context.RequestMessage; + } + + #endregion + + #region FilterResponse + + /// + /// 过滤响应 + /// + /// 响应消息 + protected virtual HttpResponseMessage FilterResponse( HttpResponseMessage response ) { + var context = new ResponseContext( response, Util.Helpers.Web.HttpContext ); + var responseFilters = Options?.ServiceInvocation?.ResponseFilters; + if ( responseFilters == null || responseFilters.Count == 0 ) + return response; + foreach ( var filter in responseFilters.Where( t => t is { Enabled: true } ).OrderBy( t => t.Order ) ) + filter.Handle( context ); + return context.ResponseMessage; + } + + #endregion + + #region ToResult + + /// + /// 转换结果 + /// + /// 响应消息 + /// 取消令牌 + protected virtual async Task> ToResult( HttpResponseMessage response, CancellationToken cancellationToken ) { + if ( IsUnpackResult ) + return await ToUnpackResult( response, cancellationToken ); + return await ToNotUnpackResult( response, cancellationToken ); + } + + /// + /// 转换结果 - 使用ServiceResult解包 + /// + /// 响应消息 + /// 取消令牌 + protected virtual async Task> ToUnpackResult( HttpResponseMessage response, CancellationToken cancellationToken ) { + response.EnsureSuccessStatusCode(); + if ( OnResultAction == null ) + return await response.Content.ReadFromJsonAsync>( Client.JsonSerializerOptions, cancellationToken ); + var objResult = await OnResultAction( response, Client.JsonSerializerOptions, cancellationToken ); + var result = objResult as ServiceResult; + result.CheckNull( nameof( result ) ); + return result; + } + + /// + /// 转换结果 - 不解包 + /// + /// 响应消息 + /// 取消令牌 + protected virtual async Task> ToNotUnpackResult( HttpResponseMessage response, CancellationToken cancellationToken ) { + var result = new ServiceResult { + Code = GetNotUnpackStateCode( response ) + }; + if ( result.Code != StateCode.Ok ) { + result.Message = await response.Content.ReadAsStringAsync( cancellationToken ); + return result; + } + if ( OnResultAction == null ) { + var json = await response.Content.ReadAsStringAsync( cancellationToken ); + if ( json.IsEmpty() ) + return result; + result.Data = Util.Helpers.Json.ToObject( json,Client.JsonSerializerOptions ); + return result; + } + var content = await OnResultAction( response, Client.JsonSerializerOptions, cancellationToken ); + result.Data = Util.Helpers.Convert.To( content ); + return result; + } + + /// + /// 获取未解包结果的状态码 + /// + protected virtual string GetNotUnpackStateCode( HttpResponseMessage response ) { + if ( response.IsSuccessStatusCode ) + return StateCode.Ok; + if ( response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden ) + return StateCode.Unauthorized; + return StateCode.Fail; + } + + #endregion + + #region InvokeAfter + + /// + /// 调用方法后操作 + /// + protected virtual async Task InvokeAfter( string methodName, HttpRequestMessage request, HttpResponseMessage response, ServiceResult result ) { + var state = ToState( result ); + if ( state == ServiceState.Ok ) { + await SuccessHandlerAsync( methodName, request, response, result.Data ); + return; + } + if ( state == ServiceState.Fail ) { + await FailHandlerAsync( methodName, request, response, null, result?.Message ); + return; + } + if ( state == ServiceState.Unauthorized ) { + await UnauthorizedHandlerAsync( methodName, request, response ); + return; + } + } + + #endregion + + #region ToState + + /// + /// 转换为服务状态 + /// + protected virtual ServiceState ToState( ServiceResult result ) { + if ( result == null ) + return ServiceState.Fail; + if ( OnStateAction != null ) + return OnStateAction( result.Code ); + switch ( result.Code ) { + case StateCode.Ok: + return ServiceState.Ok; + case StateCode.Unauthorized: + return ServiceState.Unauthorized; + default: + return ServiceState.Fail; + } + } + + #endregion + + #region SuccessHandlerAsync + + /// + /// 成功处理操作 + /// + protected virtual async Task SuccessHandlerAsync( string methodName, HttpRequestMessage request, HttpResponseMessage response, TResponse result ) { + Log.LogDebug( "调用服务成功,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + if ( OnSuccessAction != null ) { + await OnSuccessAction( request, response, result ); + return; + } + if ( Options?.ServiceInvocation?.OnSuccess == null ) + return; + await Options.ServiceInvocation.OnSuccess( request, response, result ); + } + + #endregion + + #region FailHandlerAsync + + /// + /// 失败处理操作 + /// + protected virtual async Task FailHandlerAsync( string methodName, HttpRequestMessage request, HttpResponseMessage response, Exception exception, string message ) { + if ( exception == null ) + Log.LogWarning( "调用服务失败,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + else { + Log.LogError( exception, "调用服务失败,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + } + if ( OnFailAction != null ) { + await OnFailAction( request, response, exception ); + return; + } + if ( Options?.ServiceInvocation?.OnFail != null ) { + await Options.ServiceInvocation.OnFail( request, response, exception ); + return; + } + if ( exception != null ) + throw new InvocationException( AppId, methodName, exception, response ); + throw new Warning( message ); + } + + #endregion + + #region UnauthorizedHandlerAsync + + /// + /// 未授权处理操作 + /// + protected virtual async Task UnauthorizedHandlerAsync( string methodName, HttpRequestMessage request, HttpResponseMessage response ) { + Log.LogWarning( "调用未授权的服务,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + if ( OnUnauthorizedAction != null ) { + await OnUnauthorizedAction( request, response ); + return; + } + if ( Options?.ServiceInvocation?.OnUnauthorized != null ) { + await Options.ServiceInvocation.OnUnauthorized( request, response ); + return; + } + throw new Warning( R.UnauthorizedMessage, code: StateCode.Unauthorized ); + } + + #endregion + + #region CompleteHandlerAsync + + /// + /// 调用完成操作 + /// + protected virtual async Task CompleteHandlerAsync( string methodName, HttpRequestMessage request, HttpResponseMessage response ) { + Log.LogTrace( "调用服务完成,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + if ( OnCompleteAction != null ) { + await OnCompleteAction( request, response ); + return; + } + if ( Options?.ServiceInvocation?.OnComplete == null ) + return; + await Options.ServiceInvocation.OnComplete( request, response ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocationBase.cs b/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocationBase.cs new file mode 100644 index 000000000..92094d0b8 --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocationBase.cs @@ -0,0 +1,140 @@ +namespace Util.Microservices.Dapr.ServiceInvocations; + +/// +/// Dapr服务调用操作 +/// +/// 服务调用接口类型 +public abstract class DaprServiceInvocationBase : IServiceInvocationBase where TServiceInvocation : IServiceInvocationBase { + /// + /// Dapr客户端 + /// + protected DaprClient Client { get; } + /// + /// 配置 + /// + protected DaprOptions Options { get; } + /// + /// 日志 + /// + protected ILogger Log { get; set; } + /// + /// 应用标识 + /// + protected string AppId { get; set; } + /// + /// 是否对结果解包 + /// + protected bool IsUnpackResult { get; set; } + /// + /// 请求头集合 + /// + protected IDictionary Headers { get; set; } + /// + /// 导入请求头键名集合 + /// + protected List ImportHeaderKeys { get; set; } + /// + /// 移除请求头键名集合 + /// + protected IList RemoveHeaderKeys { get; set; } + /// + /// 服务状态码转换事件 + /// + protected Func OnStateAction { get; set; } + + /// + /// 初始化Dapr服务调用操作 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + protected DaprServiceInvocationBase( DaprClient client,IOptions options, ILoggerFactory loggerFactory ) { + Client = client ?? throw new ArgumentNullException( nameof( client ) ); + Options = options?.Value ?? new DaprOptions(); + Log = loggerFactory?.CreateLogger( typeof( IServiceInvocation ) ) ?? NullLogger.Instance; + Headers = new Dictionary(); + ImportHeaderKeys = new List(); + RemoveHeaderKeys = new List(); + IsUnpackResult = Options.ServiceInvocation.IsUnpackResult; + } + + /// + public TServiceInvocation Service( string appId ) { + AppId = appId; + return Return(); + } + + /// + /// 返回 + /// + private TServiceInvocation Return() { + return (TServiceInvocation)(object)this; + } + + /// + public TServiceInvocation UnpackResult( bool isUnpack ) { + IsUnpackResult = isUnpack; + return Return(); + } + + /// + public TServiceInvocation BearerToken( string token ) { + Header( "Authorization",$"Bearer {token}" ); + return Return(); + } + + /// + public TServiceInvocation Header( string key, string value ) { + if ( key.IsEmpty() ) + return Return(); + if ( Headers.ContainsKey( key ) ) + Headers.Remove( key ); + Headers.Add( key, value ); + return Return(); + } + + /// + public TServiceInvocation Header( IDictionary headers ) { + if ( headers == null ) + return Return(); + foreach ( var header in headers ) + Header( header.Key, header.Value ); + return Return(); + } + + /// + public TServiceInvocation ImportHeader( string key ) { + if ( key.IsEmpty() ) + return Return(); + if( ImportHeaderKeys.Contains( key ) == false ) + ImportHeaderKeys.Add( key ); + return Return(); + } + + /// + public TServiceInvocation ImportHeader( IEnumerable keys ) { + if ( keys == null ) + return Return(); + foreach ( var key in keys ) + ImportHeader( key ); + return Return(); + } + + /// + public TServiceInvocation RemoveHeader( string key ) { + if ( key.IsEmpty() ) + return Return(); + if ( RemoveHeaderKeys.Contains( key ) == false ) + RemoveHeaderKeys.Add( key ); + return Return(); + } + + /// + /// 服务状态码转换事件 + /// + /// 转换为服务状态码,参数为业务状态码或Http状态码,取决于是否使用约定结果类型 + public TServiceInvocation OnState( Func action ) { + OnStateAction = action; + return Return(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocations/RequestFilterBase.cs b/src/Util.Microservices.Dapr/ServiceInvocations/RequestFilterBase.cs new file mode 100644 index 000000000..100c6edce --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocations/RequestFilterBase.cs @@ -0,0 +1,20 @@ +namespace Util.Microservices.Dapr.ServiceInvocations; + +/// +/// WebApi服务调用请求过滤器 +/// +public abstract class RequestFilterBase : IRequestFilter { + /// + /// 是否启用 + /// + public bool Enabled => true; + /// + /// 序号 + /// + public int Order => 0; + /// + /// 处理请求 + /// + /// 请求上下文 + public abstract void Handle( RequestContext context ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocations/ResponseFilterBase.cs b/src/Util.Microservices.Dapr/ServiceInvocations/ResponseFilterBase.cs new file mode 100644 index 000000000..ff1a965e3 --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocations/ResponseFilterBase.cs @@ -0,0 +1,20 @@ +namespace Util.Microservices.Dapr.ServiceInvocations; + +/// +/// WebApi服务调用响应过滤器 +/// +public abstract class ResponseFilterBase : IResponseFilter { + /// + /// 是否启用 + /// + public bool Enabled => true; + /// + /// 序号 + /// + public int Order => 0; + /// + /// 处理响应 + /// + /// 响应上下文 + public abstract void Handle( ResponseContext context ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Usings.cs b/src/Util.Microservices.Dapr/Usings.cs new file mode 100644 index 000000000..5ee3b77e9 --- /dev/null +++ b/src/Util.Microservices.Dapr/Usings.cs @@ -0,0 +1,22 @@ +global using System; +global using System.Threading.Tasks; +global using System.Threading; +global using System.Globalization; +global using System.Net.Http; +global using System.Linq; +global using System.Collections.Generic; +global using System.Net.Http.Json; +global using System.Text.Json; +global using Microsoft.Extensions.Primitives; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using Microsoft.Extensions.Logging.Abstractions; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.AspNetCore.WebUtilities; +global using Dapr.Client; +global using Dapr.Extensions.Configuration; +global using Util.Http; +global using Util.Applications; +global using Util.Exceptions; +global using Util.Properties; \ No newline at end of file diff --git a/src/Util.Microservices.HealthChecks/04-Util.Microservices.HealthChecks.csproj b/src/Util.Microservices.HealthChecks/04-Util.Microservices.HealthChecks.csproj new file mode 100644 index 000000000..40a87ed3a --- /dev/null +++ b/src/Util.Microservices.HealthChecks/04-Util.Microservices.HealthChecks.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Microservices.HealthChecks + Util.Microservices.HealthChecks + Util.Microservices.HealthChecks是Util应用框架的健康检查操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Microservices.HealthChecks.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Microservices.HealthChecks.xml + + + + + True + False + + + + + + + + + + + + + + + + + diff --git a/src/Util.Microservices.HealthChecks/Dapr/DaprHealthCheck.cs b/src/Util.Microservices.HealthChecks/Dapr/DaprHealthCheck.cs new file mode 100644 index 000000000..2276c425c --- /dev/null +++ b/src/Util.Microservices.HealthChecks/Dapr/DaprHealthCheck.cs @@ -0,0 +1,34 @@ +namespace Util.Microservices.HealthChecks.Dapr; + +/// +/// Dapr健康检查 +/// +public class DaprHealthCheck : IHealthCheck { + /// + /// Dapr客户端 + /// + private readonly DaprClient _client; + + /// + /// 初始化Dapr健康检查 + /// + /// Dapr客户端 + public DaprHealthCheck( DaprClient client ) { + _client = client ?? throw new ArgumentNullException( nameof( client ) ); + } + + /// + /// 健康检查 + /// + /// 健康检查上下文 + /// 取消令牌 + public async Task CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default ) { + try { + var healthy = await _client.CheckHealthAsync( cancellationToken ); + return healthy ? HealthCheckResult.Healthy() : new HealthCheckResult( context.Registration.FailureStatus ); + } + catch ( Exception exception ) { + return HealthCheckResult.Unhealthy( exception.Message, exception ); + } + } +} diff --git a/src/Util.Microservices.HealthChecks/EntityFrameworkCore/UnitOfWorkHealthCheck.cs b/src/Util.Microservices.HealthChecks/EntityFrameworkCore/UnitOfWorkHealthCheck.cs new file mode 100644 index 000000000..20e732779 --- /dev/null +++ b/src/Util.Microservices.HealthChecks/EntityFrameworkCore/UnitOfWorkHealthCheck.cs @@ -0,0 +1,36 @@ +namespace Util.Microservices.HealthChecks.EntityFrameworkCore; + +/// +/// Ԫ +/// +/// Ԫ +public class UnitOfWorkHealthCheck : IHealthCheck where TUnitOfWork : IUnitOfWork { + /// + /// Ԫ + /// + private readonly TUnitOfWork _unitOfWork; + + /// + /// ʼԪ + /// + /// Ԫ + public UnitOfWorkHealthCheck( TUnitOfWork unitOfWork ) { + _unitOfWork = unitOfWork ?? throw new ArgumentNullException( nameof( unitOfWork ) ); + } + + /// + /// + /// + /// + /// ȡ + public async Task CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default ) { + context.CheckNull( nameof( context ) ); + try { + var healthy = await _unitOfWork.CanConnectAsync( cancellationToken ); + return healthy ? HealthCheckResult.Healthy() : new HealthCheckResult( context.Registration.FailureStatus ); + } + catch ( Exception exception ) { + return HealthCheckResult.Unhealthy( exception.Message, exception ); + } + } +} diff --git a/src/Util.Microservices.HealthChecks/HealthCheckBuilderExtensions.cs b/src/Util.Microservices.HealthChecks/HealthCheckBuilderExtensions.cs new file mode 100644 index 000000000..7bfc153f0 --- /dev/null +++ b/src/Util.Microservices.HealthChecks/HealthCheckBuilderExtensions.cs @@ -0,0 +1,53 @@ +using Util.Microservices.HealthChecks.Dapr; +using Util.Microservices.HealthChecks.EntityFrameworkCore; + +namespace Util.Microservices.HealthChecks; + +/// +/// 健康检查生成器扩展 +/// +public static class HealthCheckBuilderExtensions { + /// + /// 配置Dapr健康检查 + /// + /// 健康检查生成器 + /// 健康检查器名称,默认值:dapr + /// 标签集合 + /// 检测失败状态,默认值:HealthStatus.Unhealthy + public static IHealthChecksBuilder AddDapr( this IHealthChecksBuilder builder, string name = "dapr", IEnumerable tags = null, HealthStatus? failureStatus = null ) { + builder.CheckNull( nameof( builder ) ); + failureStatus ??= HealthStatus.Unhealthy; + tags ??= Enumerable.Empty(); + return builder.AddCheck( name, failureStatus, tags ); + } + + /// + /// 配置工作单元健康检查 + /// + /// 健康检查生成器 + /// 健康检查器名称,默认值:unitOfWork + /// 标签集合 + /// 检测失败状态,默认值:HealthStatus.Unhealthy + public static IHealthChecksBuilder AddUnitOfWork( this IHealthChecksBuilder builder, string name = "unitOfWork", IEnumerable tags = null, HealthStatus? failureStatus = null ) + where TUnitOfWork: IUnitOfWork { + builder.CheckNull( nameof( builder ) ); + failureStatus ??= HealthStatus.Unhealthy; + tags ??= Enumerable.Empty(); + return builder.AddCheck>( name, failureStatus, tags ); + } + + /// + /// 配置Url健康检查 + /// + /// 健康检查生成器 + /// 请求地址 + /// 健康检查器名称 + /// 标签集合 + /// 检测失败状态,默认值:HealthStatus.Unhealthy + public static IHealthChecksBuilder AddUrl( this IHealthChecksBuilder builder,string url, string name, IEnumerable tags = null, HealthStatus? failureStatus = null ) { + builder.CheckNull( nameof( builder ) ); + failureStatus ??= HealthStatus.Unhealthy; + tags ??= Enumerable.Empty(); + return builder.AddUrlGroup( new Uri( url ), name, failureStatus, tags ); + } +} diff --git a/src/Util.Microservices.HealthChecks/Usings.cs b/src/Util.Microservices.HealthChecks/Usings.cs new file mode 100644 index 000000000..db51d0656 --- /dev/null +++ b/src/Util.Microservices.HealthChecks/Usings.cs @@ -0,0 +1,10 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Dapr.Client; +global using Util.Data; +global using Util.Data.EntityFrameworkCore; diff --git a/src/Util.Microservices.Polly/02-Util.Microservices.Polly.csproj b/src/Util.Microservices.Polly/02-Util.Microservices.Polly.csproj new file mode 100644 index 000000000..0cc57f2eb --- /dev/null +++ b/src/Util.Microservices.Polly/02-Util.Microservices.Polly.csproj @@ -0,0 +1,36 @@ + + + $(NetTargetFramework) + icon.jpg + Util.Microservices.Polly + Util.Microservices + Util.Microservices.Polly是Util应用框架基于Polly的弹性处理操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Microservices.Polly.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Microservices.Polly.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Microservices.Polly/IPolicy.cs b/src/Util.Microservices.Polly/IPolicy.cs new file mode 100644 index 000000000..de58966b3 --- /dev/null +++ b/src/Util.Microservices.Polly/IPolicy.cs @@ -0,0 +1,18 @@ +using Util.Dependency; + +namespace Util.Microservices; + +/// +/// 弹性处理策略 +/// +public interface IPolicy : ITransientDependency { + /// + /// 重试 + /// + IRetryPolicyHandler Retry(); + /// + /// 重试 + /// + /// 重试次数 + IRetryPolicyHandler Retry( int count ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/IRetryPolicy.TResult.cs b/src/Util.Microservices.Polly/IRetryPolicy.TResult.cs new file mode 100644 index 000000000..93085a31b --- /dev/null +++ b/src/Util.Microservices.Polly/IRetryPolicy.TResult.cs @@ -0,0 +1,35 @@ +namespace Util.Microservices; + +/// +/// 重试处理策略 +/// +public interface IRetryPolicy { + /// + /// 持续重试 + /// + IRetryPolicy Forever(); + /// + /// 重试等待 + /// + IRetryPolicy Wait(); + /// + /// 重试等待 + /// + /// 重试等待时间提供函数 + IRetryPolicy Wait( Func sleepDurationProvider ); + /// + /// 重试回调函数 + /// + /// 重试回调函数 + IRetryPolicy OnRetry( Action, int> action ); + /// + /// 执行操作 + /// + /// 操作 + TResult Execute( Func action ); + /// + /// 执行操作 + /// + /// 操作 + Task ExecuteAsync( Func> action ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/IRetryPolicy.cs b/src/Util.Microservices.Polly/IRetryPolicy.cs new file mode 100644 index 000000000..03ce11c7a --- /dev/null +++ b/src/Util.Microservices.Polly/IRetryPolicy.cs @@ -0,0 +1,35 @@ +namespace Util.Microservices; + +/// +/// 重试处理策略 +/// +public interface IRetryPolicy { + /// + /// 持续重试 + /// + IRetryPolicy Forever(); + /// + /// 重试等待 + /// + IRetryPolicy Wait(); + /// + /// 重试等待 + /// + /// 重试等待时间提供函数 + IRetryPolicy Wait( Func sleepDurationProvider ); + /// + /// 重试回调函数 + /// + /// 重试回调函数 + IRetryPolicy OnRetry( Action action ); + /// + /// 执行操作 + /// + /// 操作 + void Execute( Action action ); + /// + /// 执行操作 + /// + /// 操作 + Task ExecuteAsync( Func action ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/IRetryPolicyHandler.cs b/src/Util.Microservices.Polly/IRetryPolicyHandler.cs new file mode 100644 index 000000000..8f7440b44 --- /dev/null +++ b/src/Util.Microservices.Polly/IRetryPolicyHandler.cs @@ -0,0 +1,18 @@ +namespace Util.Microservices; + +/// +/// 重试策略处理器 +/// +public interface IRetryPolicyHandler { + /// + /// 指定触发重试的结果处理条件 + /// + /// 结果类型 + /// 触发重试的结果处理条件 + IRetryPolicy HandleResult( Func action ); + /// + /// 指定触发重试的异常 + /// + /// 异常类型 + IRetryPolicy HandleException() where TException : Exception; +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/EmptyPolicy.cs b/src/Util.Microservices.Polly/Polly/EmptyPolicy.cs new file mode 100644 index 000000000..bbe9b8216 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/EmptyPolicy.cs @@ -0,0 +1,21 @@ +namespace Util.Microservices.Polly; + +/// +/// 空弹性处理策略 +/// +public class EmptyPolicy : IPolicy { + /// + /// 空弹性处理策略实例 + /// + public static readonly IPolicy Instance = new EmptyPolicy(); + + /// + public IRetryPolicyHandler Retry() { + return EmptyRetryPolicyHandler.Instance; + } + + /// + public IRetryPolicyHandler Retry( int count ) { + return EmptyRetryPolicyHandler.Instance; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/EmptyRetryPolicy.cs b/src/Util.Microservices.Polly/Polly/EmptyRetryPolicy.cs new file mode 100644 index 000000000..78bd15e40 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/EmptyRetryPolicy.cs @@ -0,0 +1,83 @@ +namespace Util.Microservices.Polly; + +/// +/// 空重试处理策略 +/// +public class EmptyRetryPolicy : IRetryPolicy { + /// + /// 空重试处理策略实例 + /// + public static readonly IRetryPolicy Instance = new EmptyRetryPolicy(); + + /// + public IRetryPolicy Forever() { + return this; + } + + /// + public IRetryPolicy Wait() { + return this; + } + + /// + public IRetryPolicy Wait( Func sleepDurationProvider ) { + return this; + } + + /// + public IRetryPolicy OnRetry( Action action ) { + return this; + } + + /// + public void Execute( Action action ) { + action?.Invoke(); + } + + /// + public async Task ExecuteAsync( Func action ) { + if ( action == null ) + return; + await action(); + } +} + +/// +/// 空重试处理策略 +/// +public class EmptyRetryPolicy : IRetryPolicy { + /// + /// 空重试处理策略实例 + /// + public static readonly IRetryPolicy Instance = new EmptyRetryPolicy(); + + /// + public IRetryPolicy Forever() { + return this; + } + + /// + public IRetryPolicy Wait() { + return this; + } + + /// + public IRetryPolicy Wait( Func sleepDurationProvider ) { + return this; + } + + /// + public IRetryPolicy OnRetry( Action, int> action ) { + return this; + } + + /// + public TResult Execute( Func action ) { + return action == null ? default : action(); + } + + /// + public async Task ExecuteAsync( Func> action ) { + return action == null ? default : await action(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/EmptyRetryPolicyHandler.cs b/src/Util.Microservices.Polly/Polly/EmptyRetryPolicyHandler.cs new file mode 100644 index 000000000..a89901338 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/EmptyRetryPolicyHandler.cs @@ -0,0 +1,21 @@ +namespace Util.Microservices.Polly; + +/// +/// 空重试策略处理器 +/// +public class EmptyRetryPolicyHandler : IRetryPolicyHandler { + /// + /// 空重试策略处理器 + /// + public static readonly IRetryPolicyHandler Instance = new EmptyRetryPolicyHandler(); + + /// + public IRetryPolicy HandleResult( Func action ) { + return EmptyRetryPolicy.Instance; + } + + /// + public IRetryPolicy HandleException() where TException : Exception { + return EmptyRetryPolicy.Instance; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/PollyPolicy.cs b/src/Util.Microservices.Polly/Polly/PollyPolicy.cs new file mode 100644 index 000000000..55355bc16 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/PollyPolicy.cs @@ -0,0 +1,19 @@ +using Util.Dependency; + +namespace Util.Microservices.Polly; + +/// +/// Polly弹性处理策略 +/// +[Ioc(1)] +public class PollyPolicy : IPolicy { + /// + public IRetryPolicyHandler Retry() { + return new PollyRetryPolicyHandler(); + } + + /// + public IRetryPolicyHandler Retry( int count ) { + return new PollyRetryPolicyHandler( count ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.TResult.cs b/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.TResult.cs new file mode 100644 index 000000000..0ba2cc78a --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.TResult.cs @@ -0,0 +1,186 @@ +namespace Util.Microservices.Polly; + +/// +/// Polly重试处理策略 +/// +public class PollyRetryPolicy : IRetryPolicy { + + #region 字段 + + /// + /// Polly策略生成器 + /// + private readonly PolicyBuilder _policyBuilder; + /// + /// 重试次数 + /// + private readonly int? _count; + /// + /// 是否持续重试 + /// + private bool _isForever; + /// + /// 是否等待 + /// + private bool _isWait; + /// + /// 重试等待时间提供函数 + /// + private Func _sleepDurationProvider; + /// + /// 重试回调函数 + /// + private Action, int> _onRetry; + + #endregion + + #region 构造方法 + + /// + /// 初始化Polly重试处理策略 + /// + /// Polly策略生成器 + /// 重试次数 + public PollyRetryPolicy( PolicyBuilder policyBuilder, int? count ) { + _policyBuilder = policyBuilder ?? throw new ArgumentNullException( nameof( policyBuilder ) ); + _count = count; + _isForever = false; + _isWait = false; + } + + #endregion + + #region Forever + + /// + public IRetryPolicy Forever() { + _isForever = true; + return this; + } + + #endregion + + #region Wait + + /// + public IRetryPolicy Wait() { + _isWait = true; + return this; + } + + /// + public IRetryPolicy Wait( Func sleepDurationProvider ) { + _isWait = true; + _sleepDurationProvider = sleepDurationProvider; + return this; + } + + #endregion + + #region OnRetry + + /// + public IRetryPolicy OnRetry( Action, int> action ) { + _onRetry = action; + return this; + } + + #endregion + + #region Execute + + /// + public TResult Execute( Func action ) { + return CreateRetryPolicy().Execute( action ); + } + + /// + /// 创建重试策略 + /// + private RetryPolicy CreateRetryPolicy() { + if ( _isWait == false ) + return CreateRetryPolicyByNoWait(); + return CreateRetryPolicyByWait(); + } + + /// + /// 创建重试策略 - 不等待 + /// + private RetryPolicy CreateRetryPolicyByNoWait() { + if ( _isForever ) + return _policyBuilder.RetryForever( OnRetry ); + return _policyBuilder.Retry( GetRetryCount(), OnRetry ); + } + + /// + /// 重试处理函数 + /// + private void OnRetry( DelegateResult result, int times ) { + if ( _onRetry == null ) + return; + _onRetry( result, times ); + } + + /// + /// 获取重试次数 + /// + private int GetRetryCount() { + return _count > 0 ? _count.SafeValue() : 1; + } + + /// + /// 创建重试策略 - 等待 + /// + private RetryPolicy CreateRetryPolicyByWait() { + if ( _isForever ) + return _policyBuilder.WaitAndRetryForever( GetSleepDurationProvider(), ( e, i, t ) => OnRetry( e, i ) ); + return _policyBuilder.WaitAndRetry( GetRetryCount(), GetSleepDurationProvider(), ( e, t, i, c ) => OnRetry( e, i ) ); + } + + /// + /// 获取重试等待时间提供函数 + /// + private Func GetSleepDurationProvider() { + if ( _sleepDurationProvider == null ) + return retryAttempt => TimeSpan.FromSeconds( Math.Pow( 2, retryAttempt ) ); + return _sleepDurationProvider; + } + + #endregion + + #region ExecuteAsync + + /// + public Task ExecuteAsync( Func> action ) { + return CreateAsyncRetryPolicy().ExecuteAsync( action ); + } + + /// + /// 创建异步重试策略 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicy() { + if ( _isWait == false ) + return CreateAsyncRetryPolicyByNoWait(); + return CreateAsyncRetryPolicyByWait(); + } + + /// + /// 创建异步重试策略 - 不等待 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicyByNoWait() { + if ( _isForever ) + return _policyBuilder.RetryForeverAsync( OnRetry ); + return _policyBuilder.RetryAsync( GetRetryCount(), OnRetry ); + } + + /// + /// 创建异步重试策略 - 等待 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicyByWait() { + if ( _isForever ) + return _policyBuilder.WaitAndRetryForeverAsync( GetSleepDurationProvider(), ( e, i, t ) => OnRetry( e, i ) ); + return _policyBuilder.WaitAndRetryAsync( GetRetryCount(), GetSleepDurationProvider(), ( e, t, i, c ) => OnRetry( e, i ) ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.cs b/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.cs new file mode 100644 index 000000000..5f2095059 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.cs @@ -0,0 +1,186 @@ +namespace Util.Microservices.Polly; + +/// +/// Polly重试处理策略 +/// +public class PollyRetryPolicy : IRetryPolicy { + + #region 字段 + + /// + /// Polly策略生成器 + /// + private readonly PolicyBuilder _policyBuilder; + /// + /// 重试次数 + /// + private readonly int? _count; + /// + /// 是否持续重试 + /// + private bool _isForever; + /// + /// 是否等待 + /// + private bool _isWait; + /// + /// 重试等待时间提供函数 + /// + private Func _sleepDurationProvider; + /// + /// 重试回调函数 + /// + private Action _onRetry; + + #endregion + + #region 构造方法 + + /// + /// 初始化Polly重试处理策略 + /// + /// Polly策略生成器 + /// 重试次数 + public PollyRetryPolicy( PolicyBuilder policyBuilder, int? count ) { + _policyBuilder = policyBuilder ?? throw new ArgumentNullException( nameof( policyBuilder ) ); + _count = count; + _isForever = false; + _isWait = false; + } + + #endregion + + #region Forever + + /// + public IRetryPolicy Forever() { + _isForever = true; + return this; + } + + #endregion + + #region Wait + + /// + public IRetryPolicy Wait() { + _isWait = true; + return this; + } + + /// + public IRetryPolicy Wait( Func sleepDurationProvider ) { + _isWait = true; + _sleepDurationProvider = sleepDurationProvider; + return this; + } + + #endregion + + #region OnRetry + + /// + public IRetryPolicy OnRetry( Action action ) { + _onRetry = action; + return this; + } + + #endregion + + #region Execute + + /// + public void Execute( Action action ) { + CreateRetryPolicy().Execute( action ); + } + + /// + /// 创建重试策略 + /// + private RetryPolicy CreateRetryPolicy() { + if ( _isWait == false ) + return CreateRetryPolicyByNoWait(); + return CreateRetryPolicyByWait(); + } + + /// + /// 创建重试策略 - 不等待 + /// + private RetryPolicy CreateRetryPolicyByNoWait() { + if ( _isForever ) + return _policyBuilder.RetryForever( OnRetry ); + return _policyBuilder.Retry( GetRetryCount(), OnRetry ); + } + + /// + /// 重试处理函数 + /// + private void OnRetry( Exception exception, int times ) { + if ( _onRetry == null ) + return; + _onRetry( exception, times ); + } + + /// + /// 获取重试次数 + /// + private int GetRetryCount() { + return _count > 0 ? _count.SafeValue() : 1; + } + + /// + /// 创建重试策略 - 等待 + /// + private RetryPolicy CreateRetryPolicyByWait() { + if ( _isForever ) + return _policyBuilder.WaitAndRetryForever( GetSleepDurationProvider(), ( e, i, t ) => OnRetry( e, i ) ); + return _policyBuilder.WaitAndRetry( GetRetryCount(), GetSleepDurationProvider(), ( e, t, i, c ) => OnRetry( e, i ) ); + } + + /// + /// 获取重试等待时间提供函数 + /// + private Func GetSleepDurationProvider() { + if ( _sleepDurationProvider == null ) + return retryAttempt => TimeSpan.FromSeconds( Math.Pow( 2, retryAttempt ) ); + return _sleepDurationProvider; + } + + #endregion + + #region ExecuteAsync + + /// + public Task ExecuteAsync( Func action ) { + return CreateAsyncRetryPolicy().ExecuteAsync( action ); + } + + /// + /// 创建异步重试策略 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicy() { + if ( _isWait == false ) + return CreateAsyncRetryPolicyByNoWait(); + return CreateAsyncRetryPolicyByWait(); + } + + /// + /// 创建异步重试策略 - 不等待 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicyByNoWait() { + if ( _isForever ) + return _policyBuilder.RetryForeverAsync( OnRetry ); + return _policyBuilder.RetryAsync( GetRetryCount(), OnRetry ); + } + + /// + /// 创建异步重试策略 - 等待 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicyByWait() { + if ( _isForever ) + return _policyBuilder.WaitAndRetryForeverAsync( GetSleepDurationProvider(), ( e, i, t ) => OnRetry( e, i ) ); + return _policyBuilder.WaitAndRetryAsync( GetRetryCount(), GetSleepDurationProvider(), ( e, t, i, c ) => OnRetry( e, i ) ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/PollyRetryPolicyHandler.cs b/src/Util.Microservices.Polly/Polly/PollyRetryPolicyHandler.cs new file mode 100644 index 000000000..93166102d --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/PollyRetryPolicyHandler.cs @@ -0,0 +1,29 @@ +namespace Util.Microservices.Polly; + +/// +/// Polly重试策略处理器 +/// +public class PollyRetryPolicyHandler : IRetryPolicyHandler { + /// + /// 重试次数 + /// + private readonly int? _count; + + /// + /// 初始化Polly重试策略处理器 + /// + /// 重试次数 + public PollyRetryPolicyHandler( int? count = null ) { + _count = count; + } + + /// + public IRetryPolicy HandleResult( Func action ) { + return new PollyRetryPolicy( Policy.HandleResult( action ), _count ); + } + + /// + public IRetryPolicy HandleException() where TException : Exception { + return new PollyRetryPolicy( Policy.Handle(), _count ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Usings.cs b/src/Util.Microservices.Polly/Usings.cs new file mode 100644 index 000000000..f266e098d --- /dev/null +++ b/src/Util.Microservices.Polly/Usings.cs @@ -0,0 +1,4 @@ +global using System; +global using System.Threading.Tasks; +global using Polly; +global using Polly.Retry; \ No newline at end of file diff --git a/src/Util.Microservices/01-Util.Microservices.csproj b/src/Util.Microservices/01-Util.Microservices.csproj new file mode 100644 index 000000000..18167ee5a --- /dev/null +++ b/src/Util.Microservices/01-Util.Microservices.csproj @@ -0,0 +1,36 @@ + + + $(NetTargetFramework) + icon.jpg + Util.Microservices + Util.Microservices + Util.Microservices是Util应用框架的微服务操作核心类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Microservices.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Microservices.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Microservices/IMicroserviceClient.cs b/src/Util.Microservices/IMicroserviceClient.cs new file mode 100644 index 000000000..dee1d227e --- /dev/null +++ b/src/Util.Microservices/IMicroserviceClient.cs @@ -0,0 +1,17 @@ +using Util.Http; + +namespace Util.Microservices; + +/// +/// 微服务客户端 +/// +public interface IMicroserviceClient { + /// + /// Http客户端 + /// + IHttpClient HttpClient { get; } + /// + /// WebApi服务调用 + /// + IServiceInvocation ServiceInvocation { get; } +} \ No newline at end of file diff --git a/src/Util.Microservices/IMicroserviceClientFactory.cs b/src/Util.Microservices/IMicroserviceClientFactory.cs new file mode 100644 index 000000000..c77b99e62 --- /dev/null +++ b/src/Util.Microservices/IMicroserviceClientFactory.cs @@ -0,0 +1,11 @@ +namespace Util.Microservices; + +/// +/// 微服务客户端工厂 +/// +public interface IMicroserviceClientFactory : ITransientDependency { + /// + /// 创建微服务客户端 + /// + IMicroserviceClient Create(); +} \ No newline at end of file diff --git a/src/Util.Microservices/IRequestFilter.cs b/src/Util.Microservices/IRequestFilter.cs new file mode 100644 index 000000000..e55a82872 --- /dev/null +++ b/src/Util.Microservices/IRequestFilter.cs @@ -0,0 +1,20 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用请求过滤器 +/// +public interface IRequestFilter { + /// + /// 是否启用 + /// + bool Enabled { get; } + /// + /// 序号 + /// + int Order { get; } + /// + /// 处理请求 + /// + /// 请求上下文 + void Handle( RequestContext context ); +} \ No newline at end of file diff --git a/src/Util.Microservices/IResponseFilter.cs b/src/Util.Microservices/IResponseFilter.cs new file mode 100644 index 000000000..1e1dadab7 --- /dev/null +++ b/src/Util.Microservices/IResponseFilter.cs @@ -0,0 +1,20 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用响应过滤器 +/// +public interface IResponseFilter { + /// + /// 是否启用 + /// + bool Enabled { get; } + /// + /// 序号 + /// + int Order { get; } + /// + /// 处理响应 + /// + /// 响应上下文 + void Handle( ResponseContext context ); +} \ No newline at end of file diff --git a/src/Util.Microservices/IServiceInvocation.cs b/src/Util.Microservices/IServiceInvocation.cs new file mode 100644 index 000000000..71b14bf99 --- /dev/null +++ b/src/Util.Microservices/IServiceInvocation.cs @@ -0,0 +1,114 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用 +/// +public interface IServiceInvocation : IServiceInvocationBase,ITransientDependency { + /// + /// 调用前事件 + /// + /// 调用前操作,返回false取消调用 + IServiceInvocation OnBefore( Func action ); + /// + /// 响应结果转换事件 + /// + /// 响应结果转换操作 + IServiceInvocation OnResult( Func> action ); + /// + /// 调用后事件 + /// + /// 调用后处理操作 + IServiceInvocation OnAfter( Action action ); + /// + /// 调用成功事件 + /// + /// 响应结果类型 + /// 调用成功处理操作 + IServiceInvocation OnSuccess( Func action ); + /// + /// 调用失败事件 + /// + /// 调用失败处理操作 + IServiceInvocation OnFail( Func action ); + /// + /// 调用未授权事件 + /// + /// 调用未授权处理操作 + IServiceInvocation OnUnauthorized( Func action ); + /// + /// 调用完成事件 + /// + /// 调用完成处理操作 + IServiceInvocation OnComplete( Func action ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 取消令牌 + Task InvokeAsync( string methodName, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, HttpMethod httpMethod, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// 取消令牌 + Task InvokeAsync( string methodName,object data, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, object data, HttpMethod httpMethod, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 取消令牌 + Task InvokeAsync( string methodName, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, HttpMethod httpMethod, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// 取消令牌 + Task InvokeAsync( string methodName, object data, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, object data, HttpMethod httpMethod, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// 取消令牌 + Task InvokeAsync( string methodName, TRequest data, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, TRequest data, HttpMethod httpMethod, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Microservices/IServiceInvocationBase.cs b/src/Util.Microservices/IServiceInvocationBase.cs new file mode 100644 index 000000000..f158fbbd5 --- /dev/null +++ b/src/Util.Microservices/IServiceInvocationBase.cs @@ -0,0 +1,54 @@ +namespace Util.Microservices; + +/// +/// 服务调用 +/// +/// 服务调用接口类型 +public interface IServiceInvocationBase where TServiceInvocation : IServiceInvocationBase { + /// + /// 设置应用标识,即服务名 + /// + /// 应用标识,即服务名 + TServiceInvocation Service( string appId ); + /// + /// 设置或取消对约定结果进行解包处理,默认为true + /// + /// 是否对结果进行解包处理 + TServiceInvocation UnpackResult( bool isUnpack ); + /// + /// 设置访问令牌 + /// + /// 访问令牌 + TServiceInvocation BearerToken( string token ); + /// + /// 设置请求头 + /// + /// 键 + /// 值 + TServiceInvocation Header( string key, string value ); + /// + /// 设置请求头 + /// + /// 请求头键值对集合 + TServiceInvocation Header( IDictionary headers ); + /// + /// 从当前HttpContext导入请求头 + /// + /// 请求头键名 + TServiceInvocation ImportHeader( string key ); + /// + /// 从当前HttpContext导入请求头 + /// + /// 请求头键名集合 + TServiceInvocation ImportHeader( IEnumerable keys ); + /// + /// 移除请求头 + /// + /// 请求头键名 + TServiceInvocation RemoveHeader( string key ); + /// + /// 服务状态码转换事件 + /// + /// 转换为服务状态码,参数为业务状态码或Http状态码,取决于是否使用约定结果类型 + TServiceInvocation OnState( Func action ); +} \ No newline at end of file diff --git a/src/Util.Microservices/RequestContext.cs b/src/Util.Microservices/RequestContext.cs new file mode 100644 index 000000000..efad92337 --- /dev/null +++ b/src/Util.Microservices/RequestContext.cs @@ -0,0 +1,26 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用请求上下文 +/// +public class RequestContext { + /// + /// 初始化WebApi服务调用请求上下文 + /// + /// 请求消息 + /// Http上下文 + public RequestContext( HttpRequestMessage requestMessage, HttpContext context ) { + RequestMessage = requestMessage; + HttpContext = context; + } + + /// + /// 请求消息 + /// + public HttpRequestMessage RequestMessage { get; } + + /// + /// Http上下文 + /// + public HttpContext HttpContext { get; } +} \ No newline at end of file diff --git a/src/Util.Microservices/ResponseContext.cs b/src/Util.Microservices/ResponseContext.cs new file mode 100644 index 000000000..51d2baec0 --- /dev/null +++ b/src/Util.Microservices/ResponseContext.cs @@ -0,0 +1,26 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用响应上下文 +/// +public class ResponseContext { + /// + /// 初始化WebApi服务调用响应上下文 + /// + /// 响应消息 + /// Http上下文 + public ResponseContext( HttpResponseMessage responseMessage, HttpContext context ) { + ResponseMessage = responseMessage; + HttpContext = context; + } + + /// + /// 响应消息 + /// + public HttpResponseMessage ResponseMessage { get; } + + /// + /// Http上下文 + /// + public HttpContext HttpContext { get; } +} \ No newline at end of file diff --git a/src/Util.Microservices/ServiceResult.cs b/src/Util.Microservices/ServiceResult.cs new file mode 100644 index 000000000..69d14f2fb --- /dev/null +++ b/src/Util.Microservices/ServiceResult.cs @@ -0,0 +1,19 @@ +namespace Util.Microservices; + +/// +/// 服务约定结果 +/// +public class ServiceResult { + /// + /// 状态码 + /// + public string Code { get; set; } + /// + /// 消息 + /// + public string Message { get; set; } + /// + /// 数据 + /// + public TData Data { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices/ServiceState.cs b/src/Util.Microservices/ServiceState.cs new file mode 100644 index 000000000..9ce4a42fd --- /dev/null +++ b/src/Util.Microservices/ServiceState.cs @@ -0,0 +1,19 @@ +namespace Util.Microservices; + +/// +/// 服务状态码 +/// +public enum ServiceState { + /// + /// 失败 + /// + Fail, + /// + /// 成功 + /// + Ok, + /// + /// 未授权 + /// + Unauthorized +} \ No newline at end of file diff --git a/src/Util.Microservices/Usings.cs b/src/Util.Microservices/Usings.cs new file mode 100644 index 000000000..5e165b3a2 --- /dev/null +++ b/src/Util.Microservices/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using System.Collections.Generic; +global using System.Threading.Tasks; +global using System.Threading; +global using System.Net.Http; +global using System.Text.Json; +global using Microsoft.AspNetCore.Http; +global using Util.Dependency; \ No newline at end of file diff --git a/src/Util.ObjectMapping.AutoMapper/Usings.cs b/src/Util.ObjectMapping.AutoMapper/Usings.cs index 369eecd4b..a442e3fe0 100644 --- a/src/Util.ObjectMapping.AutoMapper/Usings.cs +++ b/src/Util.ObjectMapping.AutoMapper/Usings.cs @@ -1,28 +1,6 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using AutoMapper; global using AutoMapper.Internal; diff --git a/src/Util.Scheduling.Hangfire/03-Util.Scheduling.Hangfire.csproj b/src/Util.Scheduling.Hangfire/03-Util.Scheduling.Hangfire.csproj index cbd21f8bb..de3c2faca 100644 --- a/src/Util.Scheduling.Hangfire/03-Util.Scheduling.Hangfire.csproj +++ b/src/Util.Scheduling.Hangfire/03-Util.Scheduling.Hangfire.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/Util.Scheduling.Hangfire/HangfireScheduler.cs b/src/Util.Scheduling.Hangfire/HangfireScheduler.cs index 4d86d77dc..db2e6c401 100644 --- a/src/Util.Scheduling.Hangfire/HangfireScheduler.cs +++ b/src/Util.Scheduling.Hangfire/HangfireScheduler.cs @@ -22,13 +22,13 @@ public HangfireScheduler( BackgroundJobServerOptions options ) { } /// - public Task StartAsync( CancellationToken cancellationToken = default ) { + public virtual Task StartAsync( CancellationToken cancellationToken = default ) { _jobServer = new BackgroundJobServer( _options ); return Task.CompletedTask; } /// - public Task StopAsync( CancellationToken cancellationToken = default ) { + public virtual Task StopAsync( CancellationToken cancellationToken = default ) { _jobServer.Dispose(); return Task.CompletedTask; } diff --git a/src/Util.Scheduling.Hangfire/ISchedulerManagerExtensions.cs b/src/Util.Scheduling.Hangfire/ISchedulerManagerExtensions.cs index 953fc5e07..08cbcd3bf 100644 --- a/src/Util.Scheduling.Hangfire/ISchedulerManagerExtensions.cs +++ b/src/Util.Scheduling.Hangfire/ISchedulerManagerExtensions.cs @@ -204,51 +204,6 @@ public static string Continue( this ISchedulerManager source, string parentId return BackgroundJob.ContinueJobWith( parentId, actionExpression, options ); } - /// - /// 执行重复操作 - /// - /// 后台任务调度管理器 - /// 后台操作 - /// Cron表达式 - /// 队列名称 - public static void Repeat( this ISchedulerManager source, Expression actionExpression, string cron,string queue = "default" ) { - RecurringJob.AddOrUpdate( actionExpression, cron,queue: queue ); - } - - /// - /// 执行重复操作 - /// - /// 后台任务调度管理器 - /// 后台操作 - /// Cron表达式 - /// 队列名称 - public static void Repeat( this ISchedulerManager source, Expression> actionExpression, string cron, string queue = "default" ) { - RecurringJob.AddOrUpdate( actionExpression, cron, queue: queue ); - } - - /// - /// 执行重复操作 - /// - /// 后台任务调度管理器 - /// 后台操作 - /// Cron表达式 - /// 队列名称 - public static void Repeat( this ISchedulerManager source, Expression> actionExpression, string cron, string queue = "default" ) { - RecurringJob.AddOrUpdate( actionExpression, cron, queue: queue ); - } - - /// - /// 执行重复操作 - /// - /// 后台任务调度管理器 - /// 后台操作 - /// Cron表达式 - /// 队列名称 - public static void Repeat( this ISchedulerManager source, Expression> actionExpression, string cron, string queue = "default" ) { - RecurringJob.AddOrUpdate( actionExpression, cron, queue: queue ); - } - - /// /// 执行重复操作 /// @@ -257,8 +212,13 @@ public static void Repeat( this ISchedulerManager source, Expression后台操作 /// Cron表达式 /// 队列名称 - public static void Repeat( this ISchedulerManager source, string id, Expression actionExpression, string cron, string queue = "default" ) { - RecurringJob.AddOrUpdate( id, actionExpression, cron, queue: queue ); + /// 任务配置 + public static void Repeat( this ISchedulerManager source, string id, Expression actionExpression, string cron, string queue = "default", RecurringJobOptions options = null ) { + if ( options == null ) { + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron ); + return; + } + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron, options ); } /// @@ -269,8 +229,13 @@ public static void Repeat( this ISchedulerManager source, string id, Expression< /// 后台操作 /// Cron表达式 /// 队列名称 - public static void Repeat( this ISchedulerManager source, string id, Expression> actionExpression, string cron, string queue = "default" ) { - RecurringJob.AddOrUpdate( id, actionExpression, cron, queue: queue ); + /// 任务配置 + public static void Repeat( this ISchedulerManager source, string id, Expression> actionExpression, string cron, string queue = "default", RecurringJobOptions options = null ) { + if ( options == null ) { + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron ); + return; + } + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron, options ); } /// @@ -281,8 +246,13 @@ public static void Repeat( this ISchedulerManager source, string id, Expression< /// 后台操作 /// Cron表达式 /// 队列名称 - public static void Repeat( this ISchedulerManager source, string id, Expression> actionExpression, string cron, string queue = "default" ) { - RecurringJob.AddOrUpdate( id, actionExpression, cron, queue: queue ); + /// 任务配置 + public static void Repeat( this ISchedulerManager source, string id, Expression> actionExpression, string cron, string queue = "default", RecurringJobOptions options = null ) { + if ( options == null ) { + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron ); + return; + } + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron, options ); } /// @@ -293,8 +263,13 @@ public static void Repeat( this ISchedulerManager source, string id, Expressi /// 后台操作 /// Cron表达式 /// 队列名称 - public static void Repeat( this ISchedulerManager source, string id, Expression> actionExpression, string cron, string queue = "default" ) { - RecurringJob.AddOrUpdate( id, actionExpression, cron, queue: queue ); + /// 任务配置 + public static void Repeat( this ISchedulerManager source, string id, Expression> actionExpression, string cron, string queue = "default", RecurringJobOptions options = null ) { + if ( options == null ) { + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron ); + return; + } + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron, options ); } /// diff --git a/src/Util.Scheduling.Hangfire/Usings.cs b/src/Util.Scheduling.Hangfire/Usings.cs index 0ec3c8486..06d6dcc0a 100644 --- a/src/Util.Scheduling.Hangfire/Usings.cs +++ b/src/Util.Scheduling.Hangfire/Usings.cs @@ -1,28 +1,7 @@ global using System; global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Hangfire; diff --git a/src/Util.Scheduling.Quartz/Usings.cs b/src/Util.Scheduling.Quartz/Usings.cs index d10dd2a8c..7224f3c14 100644 --- a/src/Util.Scheduling.Quartz/Usings.cs +++ b/src/Util.Scheduling.Quartz/Usings.cs @@ -1,28 +1,6 @@ global using System; global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Quartz; diff --git a/src/Util.Scheduling/Usings.cs b/src/Util.Scheduling/Usings.cs index 475e164d1..8b8642015 100644 --- a/src/Util.Scheduling/Usings.cs +++ b/src/Util.Scheduling/Usings.cs @@ -3,26 +3,6 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/src/Util.Security/Usings.cs b/src/Util.Security/Usings.cs index 82845070f..f3c7baf58 100644 --- a/src/Util.Security/Usings.cs +++ b/src/Util.Security/Usings.cs @@ -1,30 +1,7 @@ global using System; global using System.Threading.Tasks; -global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using System.Security.Cryptography; global using System.Security.Claims; global using System.Security.Principal; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.Templates.Handlebars/Usings.cs b/src/Util.Templates.Handlebars/Usings.cs index 4de20c554..c12b94586 100644 --- a/src/Util.Templates.Handlebars/Usings.cs +++ b/src/Util.Templates.Handlebars/Usings.cs @@ -1,29 +1,5 @@ global using System; global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using HandlebarsDotNet; \ No newline at end of file diff --git a/src/Util.Templates.Razor/02-Util.Templates.Razor.csproj b/src/Util.Templates.Razor/02-Util.Templates.Razor.csproj index c08c7f298..5b501cfee 100644 --- a/src/Util.Templates.Razor/02-Util.Templates.Razor.csproj +++ b/src/Util.Templates.Razor/02-Util.Templates.Razor.csproj @@ -37,7 +37,7 @@ - + diff --git a/src/Util.Templates.Razor/Usings.cs b/src/Util.Templates.Razor/Usings.cs index b157f5481..536414893 100644 --- a/src/Util.Templates.Razor/Usings.cs +++ b/src/Util.Templates.Razor/Usings.cs @@ -2,28 +2,10 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; global using System.Text; global using System.IO; global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using RazorEngineCore; \ No newline at end of file diff --git a/src/Util.Templates/Usings.cs b/src/Util.Templates/Usings.cs index d95c3e216..aa3cc77fe 100644 --- a/src/Util.Templates/Usings.cs +++ b/src/Util.Templates/Usings.cs @@ -1,27 +1,2 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file +global using System.Threading.Tasks; \ No newline at end of file diff --git a/src/Util.Ui.Angular/Extensions/ConfigExtensions.cs b/src/Util.Ui.Angular/Extensions/ConfigExtensions.cs index 5af4d0764..560e3e180 100644 --- a/src/Util.Ui.Angular/Extensions/ConfigExtensions.cs +++ b/src/Util.Ui.Angular/Extensions/ConfigExtensions.cs @@ -11,14 +11,13 @@ public static class ConfigExtensions { /// 复制并移除标识和name /// /// 配置 - public static Config CopyRemoveId( this Config config ) { + public static Config CopyRemoveAttributes( this Config config ) { var result = config.Copy(); result.RemoveAttribute( UiConst.Id ); result.RemoveAttribute( AngularConst.RawId ); result.RemoveAttribute( UiConst.Name ); result.RemoveAttribute( AngularConst.BindName ); - result.OutputAttributes.RemoveAll( UiConst.Name ); - result.OutputAttributes.RemoveAll( AngularConst.BindName ); + result.OutputAttributes.Clear(); return result; } } \ No newline at end of file diff --git a/src/Util.Ui.Angular/Usings.cs b/src/Util.Ui.Angular/Usings.cs index 4359f9a64..f85384e33 100644 --- a/src/Util.Ui.Angular/Usings.cs +++ b/src/Util.Ui.Angular/Usings.cs @@ -1,40 +1,3 @@ global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.AspNetCore.Html; global using Microsoft.AspNetCore.Razor.TagHelpers; -global using Microsoft.AspNetCore.Mvc.ViewFeatures; -global using Microsoft.AspNetCore.Mvc.ModelBinding; -global using Microsoft.AspNetCore.Mvc; -global using Microsoft.AspNetCore.Mvc.Filters; -global using Microsoft.AspNetCore.Mvc.Razor; -global using Microsoft.AspNetCore.Mvc.RazorPages; -global using Microsoft.Extensions.Options; -global using Microsoft.AspNetCore.Mvc.Infrastructure; -global using Microsoft.AspNetCore.Mvc.ApplicationModels; -global using Humanizer; \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Usings.cs b/src/Util.Ui.NgAlain/Usings.cs index e4ffd5ffc..69e0737bf 100644 --- a/src/Util.Ui.NgAlain/Usings.cs +++ b/src/Util.Ui.NgAlain/Usings.cs @@ -1,36 +1,3 @@ -global using System; -global using System.Threading.Tasks; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; +global using System.Text; global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.AspNetCore.Html; -global using Microsoft.AspNetCore.Mvc.ModelBinding; -global using Microsoft.AspNetCore.Mvc; -global using Microsoft.AspNetCore.Mvc.Filters; -global using Microsoft.AspNetCore.Mvc.Razor; -global using Microsoft.AspNetCore.Mvc.RazorPages; -global using Microsoft.AspNetCore.Mvc.Infrastructure; -global using Microsoft.AspNetCore.Mvc.ApplicationModels; -global using Humanizer; \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/03-Util.Ui.NgZorro.csproj b/src/Util.Ui.NgZorro/03-Util.Ui.NgZorro.csproj index 869dd8b09..ee676e687 100644 --- a/src/Util.Ui.NgZorro/03-Util.Ui.NgZorro.csproj +++ b/src/Util.Ui.NgZorro/03-Util.Ui.NgZorro.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs index 67c2a420f..4dca780f8 100644 --- a/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs +++ b/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs @@ -91,7 +91,7 @@ protected void SetControlId() { private TagBuilder GetFormItem() { TagBuilder builder = new EmptyContainerTagBuilder(); if ( _config.Id == _shareConfig.Id && _shareConfig.AutoCreateFormItem == true ) - builder = new FormItemBuilder( _config.CopyRemoveId() ); + builder = new FormItemBuilder( _config.CopyRemoveAttributes() ); builder.Config(); return builder; } @@ -101,7 +101,7 @@ private TagBuilder GetFormItem() { /// private TagBuilder GetFormLabel() { if ( _config.Id == _shareConfig.Id && _shareConfig.AutoCreateFormLabel == true ) { - var builder = new FormLabelBuilder( _config.CopyRemoveId() ); + var builder = new FormLabelBuilder( _config.CopyRemoveAttributes() ); builder.Config(); SetLabelText( builder ); return builder; @@ -127,7 +127,7 @@ private void SetLabelText( FormLabelBuilder builder ) { private TagBuilder GetFormControl() { TagBuilder builder = new EmptyContainerTagBuilder(); if( _config.Id == _shareConfig.Id && _shareConfig.AutoCreateFormControl == true ) - builder = new FormControlBuilder( _config.CopyRemoveId() ); + builder = new FormControlBuilder( _config.CopyRemoveAttributes() ); builder.Config(); AppendControl( builder ); AppendValidationTempalte( builder ); @@ -146,7 +146,7 @@ protected virtual void AppendControl( TagBuilder formControlBuilder ) { private void AppendValidationTempalte( TagBuilder formControlBuilder ) { if ( _shareConfig.ValidationTempalteId.IsEmpty() ) return; - var templateBuilder = new TemplateBuilder( _config.CopyRemoveId() ); + var templateBuilder = new TemplateBuilder( _config.CopyRemoveAttributes() ); templateBuilder.Id( _shareConfig.ValidationTempalteId ); templateBuilder.SetContent( $"{{{{{_shareConfig.ValidationExtendId}.getErrorMessage()}}}}" ); formControlBuilder.AppendContent( templateBuilder ); diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputBuilder.cs b/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputBuilder.cs index 68e44172f..1561fcac6 100644 --- a/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputBuilder.cs @@ -115,6 +115,7 @@ public override InputBuilder Required() { /// public InputBuilder Events() { OnInput( _config.GetValue( UiConst.OnInput ) ); + OnEnter( _config.GetValue( UiConst.OnEnter ) ); return this; } @@ -126,6 +127,14 @@ public InputBuilder OnInput( string value ) { return this; } + /// + /// 回车事件 + /// + public InputBuilder OnEnter( string value ) { + AttributeIfNotEmpty( "(keyup.enter)", value ); + return this; + } + /// /// 配置 /// diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaBuilder.cs b/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaBuilder.cs index c68768942..80748f701 100644 --- a/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaBuilder.cs @@ -101,7 +101,24 @@ public override TextareaBuilder Required() { /// 配置事件 /// public TextareaBuilder Events() { - AttributeIfNotEmpty( "(input)", _config.GetValue( UiConst.OnInput ) ); + OnInput( _config.GetValue( UiConst.OnInput ) ); + OnEnter( _config.GetValue( UiConst.OnEnter ) ); + return this; + } + + /// + /// 输入事件 + /// + public TextareaBuilder OnInput( string value ) { + AttributeIfNotEmpty( "(input)", value ); + return this; + } + + /// + /// 回车事件 + /// + public TextareaBuilder OnEnter( string value ) { + AttributeIfNotEmpty( "(keyup.enter)", value ); return this; } diff --git a/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs b/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs index 57234d72a..488acc719 100644 --- a/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs @@ -193,6 +193,10 @@ public class InputTagHelper : FormControlTagHelperBase { /// (input),输入事件 /// public string OnInput { get; set; } + /// + /// (keyup.enter),按下回车键事件 + /// + public string OnEnter { get; set; } /// protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRenderBase.cs b/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRenderBase.cs index cc8e5ba12..d3fe2e0b8 100644 --- a/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRenderBase.cs +++ b/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRenderBase.cs @@ -102,7 +102,7 @@ private InputGroupShareConfig GetInputGroupShareConfig() { /// 获取输入框组合标签生成器 /// private TagBuilder GetInputGroupBuilder() { - var builder = new InputGroupBuilder( _config.CopyRemoveId() ); + var builder = new InputGroupBuilder( _config.CopyRemoveAttributes() ); builder.Config(); builder.Class( GetInputGroupClass() ); return builder; diff --git a/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs b/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs index 69fb5eec8..13da40d41 100644 --- a/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs @@ -121,6 +121,10 @@ public class TextareaTagHelper : FormControlTagHelperBase { /// (input),输入事件 /// public string OnInput { get; set; } + /// + /// (keyup.enter),按下回车键事件 + /// + public string OnEnter { get; set; } /// /// 渲染前操作 diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditContentLoader.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditContentLoader.cs index 41b4e4da3..cc6cf8853 100644 --- a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditContentLoader.cs +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditContentLoader.cs @@ -26,7 +26,7 @@ public void Load( TableColumnBuilder builder, IHtmlContent displayContent ) { /// 创建表格编辑列显示区域标签生成器 /// private TagBuilder CreateDisplayBuilder( TableColumnBuilder builder ) { - var result = new TableColumnDisplayBuilder( builder.GetConfig().CopyRemoveId() ); + var result = new TableColumnDisplayBuilder( builder.GetConfig().CopyRemoveAttributes() ); result.Config(); builder.AppendContent( result ); return result; @@ -36,7 +36,7 @@ private TagBuilder CreateDisplayBuilder( TableColumnBuilder builder ) { /// 创建表格编辑列控件区域标签生成器 /// private TableColumnControlBuilder CreateControlBuilder( TableColumnBuilder builder ) { - var result = new TableColumnControlBuilder( builder.GetConfig().CopyRemoveId() ); + var result = new TableColumnControlBuilder( builder.GetConfig().CopyRemoveAttributes() ); result.Config(); builder.AppendContent( result ); return result; diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyBuilder.cs index da9c8e2f3..50174bc5b 100644 --- a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyBuilder.cs @@ -48,7 +48,7 @@ public TableShareConfig GetTableShareConfig() { /// 创建表格主体行标签生成器 /// public virtual TagBuilder CreateTableBodyRowBuilder() { - return new TableBodyRowBuilder( _config.CopyRemoveId() ); + return new TableBodyRowBuilder( _config.CopyRemoveAttributes() ); } /// diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBuilder.cs index 1493420c5..8b90748b2 100644 --- a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBuilder.cs @@ -49,14 +49,14 @@ public TableShareConfig GetShareConfig() { /// 创建表头标签生成器 /// public virtual TableHeadBuilder CreateTableHeadBuilder() { - return new TableHeadBuilder( _config.CopyRemoveId() ); + return new TableHeadBuilder( _config.CopyRemoveAttributes() ); } /// /// 创建表格主体标签生成器 /// public virtual TableBodyBuilder CreateTableBodyBuilder() { - return new TableBodyBuilder( _config.CopyRemoveId() ); + return new TableBodyBuilder( _config.CopyRemoveAttributes() ); } /// diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadBuilder.cs index 701fcd0b2..aa6d565f2 100644 --- a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadBuilder.cs @@ -47,7 +47,7 @@ public TableShareConfig GetTableShareConfig() { /// 创建表头行标签生成器 /// public virtual TableHeadRowBuilder CreateTableHeadRowBuilder() { - return new TableHeadRowBuilder( _config.CopyRemoveId() ); + return new TableHeadRowBuilder( _config.CopyRemoveAttributes() ); } /// diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadRowBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadRowBuilder.cs index f1f3871ec..bb5adc0cc 100644 --- a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadRowBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadRowBuilder.cs @@ -26,7 +26,7 @@ public TableHeadRowBuilder( Config config ) : base( config ) { /// 创建表头单元格标签生成器 /// public virtual TableHeadColumnBuilder CreateTableHeadColumnBuilder() { - return new TableHeadColumnBuilder( _config.CopyRemoveId(), GetTableHeadColumnShareConfig() ); + return new TableHeadColumnBuilder( _config.CopyRemoveAttributes(), GetTableHeadColumnShareConfig() ); } /// diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyBuilder.cs index 6be66a384..a2c7dde92 100644 --- a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyBuilder.cs @@ -34,7 +34,7 @@ public override TagBuilder CreateTableBodyRowBuilder() { /// private TreeTableContainerBuilder CreateContainerBuilder() { var shareConfig = GetTableShareConfig(); - var containerBuilder = new TreeTableContainerBuilder( _config.CopyRemoveId(), CreateRowBuilder() ); + var containerBuilder = new TreeTableContainerBuilder( _config.CopyRemoveAttributes(), CreateRowBuilder() ); containerBuilder.NgFor( $"let row of {shareConfig.TableExtendId}.dataSource;index as index" ); return containerBuilder; } @@ -43,6 +43,6 @@ private TreeTableContainerBuilder CreateContainerBuilder() { /// 创建行标签生成器 /// private TreeTableBodyRowBuilder CreateRowBuilder() { - return new TreeTableBodyRowBuilder( _config.CopyRemoveId() ); + return new TreeTableBodyRowBuilder( _config.CopyRemoveAttributes() ); } } \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBuilder.cs index 2c082677d..7e99cd7ed 100644 --- a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBuilder.cs @@ -25,12 +25,12 @@ public TreeTableBuilder( Config config ) : base( config ) { /// public override TableHeadBuilder CreateTableHeadBuilder() { - return new TreeTableHeadBuilder( _config.CopyRemoveId() ); + return new TreeTableHeadBuilder( _config.CopyRemoveAttributes() ); } /// public override TableBodyBuilder CreateTableBodyBuilder() { - return new TreeTableBodyBuilder( _config.CopyRemoveId() ); + return new TreeTableBodyBuilder( _config.CopyRemoveAttributes() ); } /// diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadBuilder.cs index a3461cb7e..d658eb5c1 100644 --- a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadBuilder.cs @@ -25,6 +25,6 @@ public TreeTableHeadBuilder( Config config ) : base( config ) { /// 创建表头行标签生成器 /// public override TableHeadRowBuilder CreateTableHeadRowBuilder() { - return new TreeTableHeadRowBuilder( _config.CopyRemoveId() ); + return new TreeTableHeadRowBuilder( _config.CopyRemoveAttributes() ); } } \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadRowBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadRowBuilder.cs index 842342f61..6da5d9941 100644 --- a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadRowBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadRowBuilder.cs @@ -24,6 +24,6 @@ public TreeTableHeadRowBuilder( Config config ) : base( config ) { /// 创建表头单元格标签生成器 /// public override TableHeadColumnBuilder CreateTableHeadColumnBuilder() { - return new TreeTableHeadColumnBuilder( _config.CopyRemoveId(), GetTableHeadColumnShareConfig() ); + return new TreeTableHeadColumnBuilder( _config.CopyRemoveAttributes(), GetTableHeadColumnShareConfig() ); } } \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Data/TreeResult.cs b/src/Util.Ui.NgZorro/Data/TreeResult.cs index 88b6735fb..5491dc0a7 100644 --- a/src/Util.Ui.NgZorro/Data/TreeResult.cs +++ b/src/Util.Ui.NgZorro/Data/TreeResult.cs @@ -25,6 +25,8 @@ public TreeResult( IEnumerable data, bool async = false, bool allExpand = /// 转换为目标节点 /// protected override NgZorroTreeNode ToDestinationNode( TNode dto ) { + List children = dto.Children; + dto.Children = null; var result = new NgZorroTreeNode { Key = dto.Id, Title = dto.GetText(), @@ -37,7 +39,7 @@ protected override NgZorroTreeNode ToDestinationNode( TNode dto ) { Selected = dto.Selected.SafeValue(), IsLeaf = dto.Leaf.SafeValue(), OriginalNode = dto, - Children = dto.Children.Select( ToDestinationNode ).ToList() + Children = children.Select( ToDestinationNode ).ToList() }; return result; } diff --git a/src/Util.Ui.NgZorro/Usings.cs b/src/Util.Ui.NgZorro/Usings.cs index e4ffd5ffc..a2d3f1658 100644 --- a/src/Util.Ui.NgZorro/Usings.cs +++ b/src/Util.Ui.NgZorro/Usings.cs @@ -3,34 +3,15 @@ global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Diagnostics; global using System.Text; global using System.IO; -global using System.Reflection; global using System.ComponentModel; -global using System.Globalization; global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.Runtime.InteropServices; global using System.Text.Encodings.Web; global using System.Text.Json.Serialization; global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.AspNetCore.Html; -global using Microsoft.AspNetCore.Mvc.ModelBinding; global using Microsoft.AspNetCore.Mvc; -global using Microsoft.AspNetCore.Mvc.Filters; -global using Microsoft.AspNetCore.Mvc.Razor; -global using Microsoft.AspNetCore.Mvc.RazorPages; -global using Microsoft.AspNetCore.Mvc.Infrastructure; -global using Microsoft.AspNetCore.Mvc.ApplicationModels; -global using Humanizer; \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/WebApplicationExtensions.cs b/src/Util.Ui.NgZorro/WebApplicationExtensions.cs index 68fec311c..14be2d30a 100644 --- a/src/Util.Ui.NgZorro/WebApplicationExtensions.cs +++ b/src/Util.Ui.NgZorro/WebApplicationExtensions.cs @@ -37,7 +37,8 @@ public static WebApplication UseNgZorro( this WebApplication app, string develop app.CheckNull( nameof( app ) ); return app.UseNgZorro( spa => { spa.Options.SourcePath = "ClientApp"; - spa.UseProxyToSpaDevelopmentServer( developmentServerBaseUri ); + if ( app.Environment.IsDevelopment() ) + spa.UseProxyToSpaDevelopmentServer( developmentServerBaseUri ); } ); } } \ No newline at end of file diff --git a/src/Util.Ui/01-Util.Ui.csproj b/src/Util.Ui/01-Util.Ui.csproj index 21b131b65..044a51508 100644 --- a/src/Util.Ui/01-Util.Ui.csproj +++ b/src/Util.Ui/01-Util.Ui.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/Util.Ui/Configs/UiConst.cs b/src/Util.Ui/Configs/UiConst.cs index d07989a4f..82792cdf4 100644 --- a/src/Util.Ui/Configs/UiConst.cs +++ b/src/Util.Ui/Configs/UiConst.cs @@ -1801,6 +1801,10 @@ public static class UiConst { /// public const string OnModelChange = "on-model-change"; /// + /// 回车事件 + /// + public const string OnEnter = "on-enter"; + /// /// 隐藏 /// public const string Hidden = "hidden"; diff --git a/src/Util.Ui/Usings.cs b/src/Util.Ui/Usings.cs index 4359f9a64..8affda65c 100644 --- a/src/Util.Ui/Usings.cs +++ b/src/Util.Ui/Usings.cs @@ -2,30 +2,14 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; global using System.Diagnostics; -global using System.Text; global using System.IO; global using System.Reflection; global using System.ComponentModel; -global using System.Globalization; -global using System.Text.Json; global using System.Linq.Expressions; -global using System.Text.RegularExpressions; global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using System.Collections.Concurrent; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.AspNetCore.Html; global using Microsoft.AspNetCore.Razor.TagHelpers; global using Microsoft.AspNetCore.Mvc.ViewFeatures; diff --git a/src/Util.Validation/Usings.cs b/src/Util.Validation/Usings.cs index d95c3e216..d2c333131 100644 --- a/src/Util.Validation/Usings.cs +++ b/src/Util.Validation/Usings.cs @@ -2,26 +2,5 @@ global using System.Threading.Tasks; global using System.Collections.Generic; global using System.Linq; -global using System.Threading; -global using System.Diagnostics; -global using System.Text; -global using System.IO; -global using System.Reflection; -global using System.ComponentModel; global using System.Globalization; -global using System.Text.Json; -global using System.Linq.Expressions; -global using System.Text.RegularExpressions; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.InteropServices; -global using System.Text.Encodings.Web; -global using System.Text.Json.Serialization; -global using System.Text.Unicode; -global using System.Buffers; -global using System.Diagnostics.CodeAnalysis; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Logging.Abstractions; -global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file +global using System.ComponentModel.DataAnnotations; \ No newline at end of file diff --git a/test/Util.Application.WebApi.MySql.Tests.Integration/Util.Application.WebApi.MySql.Tests.Integration.csproj b/test/Util.Application.WebApi.MySql.Tests.Integration/Util.Application.WebApi.MySql.Tests.Integration.csproj index 579f7bda9..6604bce58 100644 --- a/test/Util.Application.WebApi.MySql.Tests.Integration/Util.Application.WebApi.MySql.Tests.Integration.csproj +++ b/test/Util.Application.WebApi.MySql.Tests.Integration/Util.Application.WebApi.MySql.Tests.Integration.csproj @@ -12,7 +12,7 @@ - + diff --git a/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Util.Application.WebApi.PostgreSql.Tests.Integration.csproj b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Util.Application.WebApi.PostgreSql.Tests.Integration.csproj index c6dc743ab..e8c3ae24a 100644 --- a/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Util.Application.WebApi.PostgreSql.Tests.Integration.csproj +++ b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Util.Application.WebApi.PostgreSql.Tests.Integration.csproj @@ -12,7 +12,7 @@ - + diff --git a/test/Util.Application.WebApi.SqlServer.Tests.Integration/Util.Application.WebApi.SqlServer.Tests.Integration.csproj b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Util.Application.WebApi.SqlServer.Tests.Integration.csproj index 1f038d55f..7f4a4b319 100644 --- a/test/Util.Application.WebApi.SqlServer.Tests.Integration/Util.Application.WebApi.SqlServer.Tests.Integration.csproj +++ b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Util.Application.WebApi.SqlServer.Tests.Integration.csproj @@ -12,7 +12,7 @@ - + diff --git a/test/Util.Application.WebApi.Tests.Integration/Util.Application.WebApi.Tests.Integration.csproj b/test/Util.Application.WebApi.Tests.Integration/Util.Application.WebApi.Tests.Integration.csproj index b1385af1b..5466dbda4 100644 --- a/test/Util.Application.WebApi.Tests.Integration/Util.Application.WebApi.Tests.Integration.csproj +++ b/test/Util.Application.WebApi.Tests.Integration/Util.Application.WebApi.Tests.Integration.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/Util.AspNetCore.Tests.Integration/Util.AspNetCore.Tests.Integration.csproj b/test/Util.AspNetCore.Tests.Integration/Util.AspNetCore.Tests.Integration.csproj index 0d79ad4f2..7a29031a2 100644 --- a/test/Util.AspNetCore.Tests.Integration/Util.AspNetCore.Tests.Integration.csproj +++ b/test/Util.AspNetCore.Tests.Integration/Util.AspNetCore.Tests.Integration.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/Util.Core.Tests.Integration/Helpers/XmlTest.cs b/test/Util.Core.Tests.Integration/Helpers/XmlTest.cs new file mode 100644 index 000000000..dc237e2d9 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Helpers/XmlTest.cs @@ -0,0 +1,207 @@ +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Util.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Tests.Helpers; + +/// +/// Xml操作测试 +/// +public class XmlTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// Xml操作 + /// + private readonly Xml _xml; + + /// + /// 测试初始化 + /// + public XmlTest( ITestOutputHelper output ) { + _output = output; + _xml = new Xml(); + } + + /// + /// 测试添加节点 + /// + [Fact] + public void TestAddNode_1() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "1" ); + result.Append( "" ); + + //操作 + _xml.AddNode( "a", "1" ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// 测试添加节点 - 父子节点 + /// + [Fact] + public void TestAddNode_2() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "2" ); + result.Append( "" ); + result.Append( "" ); + + //操作 + var parent = _xml.AddNode( "a" ); + _xml.AddNode( "b", "2", parent ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// 测试添加注释类型节点 + /// + [Fact] + public void TestAddCDataNode_1() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //操作 + _xml.AddCDataNode( "1" ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// 测试添加注释类型节点 - 父节点 + /// + [Fact] + public void TestAddCDataNode_2() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //操作 + var parent = _xml.AddNode( "a" ); + _xml.AddCDataNode( "1", parent ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// 测试添加注释类型节点 - 父节点 - 通过设置字符串父节点名称 + /// + [Fact] + public void TestAddCDataNode_3() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //操作 + _xml.AddCDataNode( "1", "a" ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// xml字符串转换为XElement列表 + /// + [Fact] + public void TestToElements_1() { + //输入 + var input = new StringBuilder(); + input.Append( "" ); + input.Append( "1" ); + input.Append( "2" ); + input.Append( "" ); + + //操作 + var elements = Xml.ToElements( input.ToString() ); + var element = elements.FirstOrDefault( t => t.Name == "b"); + + //验证 + Assert.Equal( "2",element?.Value ); + } + + /// + /// xml字符串转换为XElement列表 + /// + [Fact] + public void TestToElements_2() { + //输入 + var input = new StringBuilder(); + input.Append( "" ); + input.Append( "1" ); + input.Append( "2" ); + input.Append( "" ); + input.Append( "" ); + input.Append( "" ); + input.Append( "" ); + + //操作 + var elements = Xml.ToElements( input.ToString() ); + var element = elements.FirstOrDefault( t => t?.Name == "c" ); + + //验证 + Assert.Equal( "3", element?.Value ); + } + + /// + /// 加载Xml文件到XDocument + /// + [Fact] + public async Task TestLoadFileToDocumentAsync() { + var file = Common.GetPhysicalPath( "./Samples/version.xml" ); + var document = await Util.Helpers.Xml.LoadFileToDocumentAsync( file ); + Assert.Equal( "123", document?.Root?.Value ); + } + + /// + /// 加载Xml文件到XElement列表 + /// + [Fact] + public async Task TestLoadFileToElementsAsync() { + var file = Common.GetPhysicalPath( "./Samples/version.xml" ); + var elements = await Util.Helpers.Xml.LoadFileToElementsAsync( file ); + var element = elements.FirstOrDefault( t => t?.Name == "VersionMinor" ); + Assert.Equal( "2", element?.Value ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Samples/version.xml b/test/Util.Core.Tests.Integration/Samples/version.xml new file mode 100644 index 000000000..c49679958 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Samples/version.xml @@ -0,0 +1,5 @@ + + 1 + 2 + 3 + \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Util.Core.Tests.Integration.csproj b/test/Util.Core.Tests.Integration/Util.Core.Tests.Integration.csproj index c1d745b64..dbdc70fd3 100644 --- a/test/Util.Core.Tests.Integration/Util.Core.Tests.Integration.csproj +++ b/test/Util.Core.Tests.Integration/Util.Core.Tests.Integration.csproj @@ -41,6 +41,9 @@ Always + + Always + diff --git a/test/Util.Core.Tests/Helpers/JsonTest.cs b/test/Util.Core.Tests/Helpers/JsonTest.cs index cc2006ab0..f8ed2abc3 100644 --- a/test/Util.Core.Tests/Helpers/JsonTest.cs +++ b/test/Util.Core.Tests/Helpers/JsonTest.cs @@ -76,6 +76,27 @@ public void TestToJson_ToSingleQuotes() { Assert.Equal( result.ToString(), Json.ToJson( sample,toSingleQuotes: true ) ); } + /// + /// 测试转成Json - 序列化接口 + /// + [Fact] + public void TestToJson_Interface() { + Time.UseUtc( false ); + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"Name\":\"a\"," ); + result.Append( "\"nickname\":\"b\"," ); + result.Append( "\"firstName\":\"c\"," ); + result.Append( "\"Value\":null," ); + result.Append( "\"Date\":\"2012-12-12 20:12:12\"," ); + result.Append( "\"UtcDate\":\"2012-12-12 20:12:12\"," ); + result.Append( "\"Age\":1," ); + result.Append( "\"IsShow\":true" ); + result.Append( "}" ); + var sample = JsonTestSample.CreateToInterface(); + Assert.Equal( result.ToString(), Json.ToJson( sample ) ); + } + /// /// 测试转成Json - 异步 /// diff --git a/test/Util.Core.Tests/Samples/JsonTestSample.cs b/test/Util.Core.Tests/Samples/JsonTestSample.cs index 6ddb197f4..1ca92b399 100644 --- a/test/Util.Core.Tests/Samples/JsonTestSample.cs +++ b/test/Util.Core.Tests/Samples/JsonTestSample.cs @@ -21,7 +21,7 @@ public class C { /// /// Json测试样例 /// -public class JsonTestSample { +public class JsonTestSample : IJsonTestSample { /// /// 名称,测试公共属性,且首字母大写 /// @@ -61,7 +61,7 @@ public class JsonTestSample { public bool IsShow { get; set; } /// - /// 创建客户 + /// 创建样例 /// public static JsonTestSample Create() { return new JsonTestSample() { @@ -76,4 +76,21 @@ public static JsonTestSample Create() { IsShow = true }; } + + /// + /// 创建样例 - 转成接口 + /// + public static IJsonTestSample CreateToInterface() { + return Create(); + } +} + +/// +/// Json测试样例 +/// +public interface IJsonTestSample { + /// + /// 名称,测试公共属性,且首字母大写 + /// + public string Name { get; set; } } \ No newline at end of file diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/Util.Data.Dapper.MySql.Tests.Integration.csproj b/test/Util.Data.Dapper.MySql.Tests.Integration/Util.Data.Dapper.MySql.Tests.Integration.csproj index 76f13d1c9..76aabd3bb 100644 --- a/test/Util.Data.Dapper.MySql.Tests.Integration/Util.Data.Dapper.MySql.Tests.Integration.csproj +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/Util.Data.Dapper.MySql.Tests.Integration.csproj @@ -19,7 +19,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Util.Data.Dapper.PostgreSql.Tests.Integration.csproj b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Util.Data.Dapper.PostgreSql.Tests.Integration.csproj index 4b7850b1c..760bb33c2 100644 --- a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Util.Data.Dapper.PostgreSql.Tests.Integration.csproj +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Util.Data.Dapper.PostgreSql.Tests.Integration.csproj @@ -19,7 +19,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/Util.Data.Dapper.SqlServer.Tests.Integration.csproj b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Util.Data.Dapper.SqlServer.Tests.Integration.csproj index a730ca6c9..3d485eb5e 100644 --- a/test/Util.Data.Dapper.SqlServer.Tests.Integration/Util.Data.Dapper.SqlServer.Tests.Integration.csproj +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Util.Data.Dapper.SqlServer.Tests.Integration.csproj @@ -19,7 +19,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/EntityEventsTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/EntityEventsTest.cs index 5388978d5..1ee419f67 100644 --- a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/EntityEventsTest.cs +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/EntityEventsTest.cs @@ -1,13 +1,17 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; +using Util.Events; using Util.Tests.EventHandlers; +using Util.Tests.Events; using Util.Tests.Fakes; using Util.Tests.Infrastructure; +using Util.Tests.Models; using Util.Tests.Repositories; using Util.Tests.UnitOfWorks; using Xunit; using Xunit.Abstractions; -namespace Util.Data.EntityFrameworkCore.Tests; +namespace Util.Data.EntityFrameworkCore.Tests; /// /// 实体事件测试 @@ -28,15 +32,20 @@ public class EntityEventsTest : TestBase { /// 操作日志仓储 /// private readonly IOperationLogRepository _operationLogRepository; + /// + /// 事件总线 + /// + private readonly IEventBus _eventBus; /// /// 测试初始化 /// - public EntityEventsTest( ITestOutputHelper testOutputHelper, IProductRepository productRepository, - IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) :base(unitOfWork){ + public EntityEventsTest( ITestOutputHelper testOutputHelper, IProductRepository productRepository, + IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork, IEventBus eventBus ) : base( unitOfWork ) { _testOutputHelper = testOutputHelper; _productRepository = productRepository; _operationLogRepository = operationLogRepository; + _eventBus = eventBus; } #endregion @@ -208,4 +217,36 @@ public async Task TestEntityChangedEvent_Deleted_2() { } #endregion + + #region TestEventBus_ServiceLifetime_Transient + + /// + /// 测试内存事件总线服务生命周期 + /// + [Fact] + public async Task TestEventBus_ServiceLifetime_Transient() { + var entity = new Product( Guid.NewGuid() ) { + Code = "Code", + Name = "Name" + }; + await _productRepository.AddAsync( entity ); + await _eventBus.PublishAsync( new TestEvent() { Id = entity.Id } ); + } + + /// + /// 内存事件处理器能,获取实体 + /// + public class TestEventHandler : EventHandlerBase { + private readonly IProductRepository _repository; + public TestEventHandler( IProductRepository applicationRepository ) { + _repository = applicationRepository; + } + public override async Task HandleAsync( TestEvent @event ) { + var entity = await _repository.FindByIdAsync( @event.Id ); + Assert.NotNull( entity ); + Assert.Equal( "Name",entity.Name ); + } + } + + #endregion } \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration.csproj b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration.csproj index 5cf52076c..9ebe47c1e 100644 --- a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration.csproj +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Util.FileStorage.Minio.Tests.Integration.csproj b/test/Util.FileStorage.Minio.Tests.Integration/Util.FileStorage.Minio.Tests.Integration.csproj index df3c3f43b..65a32d423 100644 --- a/test/Util.FileStorage.Minio.Tests.Integration/Util.FileStorage.Minio.Tests.Integration.csproj +++ b/test/Util.FileStorage.Minio.Tests.Integration/Util.FileStorage.Minio.Tests.Integration.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/Util.Localization.Tests.Integration/Util.Localization.Tests.Integration.csproj b/test/Util.Localization.Tests.Integration/Util.Localization.Tests.Integration.csproj index 929d95924..c0bc81f0c 100644 --- a/test/Util.Localization.Tests.Integration/Util.Localization.Tests.Integration.csproj +++ b/test/Util.Localization.Tests.Integration/Util.Localization.Tests.Integration.csproj @@ -11,7 +11,7 @@ - + diff --git a/test/Util.Microservices.Dapr.Tests.Integration/CommandLines/DaprRunCommandTest.cs b/test/Util.Microservices.Dapr.Tests.Integration/CommandLines/DaprRunCommandTest.cs new file mode 100644 index 000000000..2f58204b2 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/CommandLines/DaprRunCommandTest.cs @@ -0,0 +1,76 @@ +namespace Util.Microservices.Dapr.Tests.CommandLines; + +/// +/// dapr run启动命令测试 +/// +public class DaprRunCommandTest { + /// + /// 输出操作 + /// + private readonly ITestOutputHelper _output; + /// + /// 日志操作 + /// + private readonly ILogger _logger; + + /// + /// 测试初始化 + /// + public DaprRunCommandTest( ITestOutputHelper output, ILogger logger ) { + _output = output; + _logger = logger; + } + + /// + /// 测试默认设置 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "dapr run --app-id app" ); + var command = DaprRunCommand.Create( "app" ).GetDebugText(); + _output.WriteLine( command ); + Assert.Contains( result.ToString(), command ); + } + + /// + /// 测试设置命令参数 + /// + [Fact] + public void TestArguments() { + var result = new StringBuilder(); + result.Append( "dapr run --app-id app " ); + result.Append( "--app-port 80 " ); + result.Append( "--app-protocol grpc " ); + result.Append( "--dapr-http-port 81 " ); + result.Append( "--dapr-grpc-port 82 " ); + result.Append( "--metrics-port 83 " ); + result.Append( "--resources-path components-path " ); + result.Append( "--config config-path " ); + result.Append( "--log-level debug " ); + result.Append( "-- dotnet run --project ../app.csproj " ); + result.Append( "--urls http://localhost:80" ); + var command = DaprRunCommand.Create( "app" ) + .UseFreePorts() + .AppPort( 80 ).AppProtocol( "grpc" ) + .DaprHttpPort( 81 ).DaprGrpcPort( 82 ).MetricsPort( 83 ) + .ComponentsPath( "components-path" ) + .ConfigPath( "config-path" ) + .Project( "../app.csproj" ) + .GetDebugText(); + Assert.Equal( result.ToString(), command ); + } + + /// + /// 执行dapr run命令 + /// + [Fact] + public void TestExecute() { + DaprRunCommand.Create( "app" ) + .Log( _logger ) + .Project( "../../../../../test/Util.Microservices.Dapr.WebApiSample/Util.Microservices.Dapr.WebApiSample.csproj" ) + .UseFreePorts() + .Execute(); + DaprStopCommand.Create( "app" ).Log( _logger ).Execute(); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalCollection.cs b/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalCollection.cs new file mode 100644 index 000000000..dc44ad212 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalCollection.cs @@ -0,0 +1,8 @@ +namespace Util.Microservices.Dapr.Tests.Fixtures; + +/// +/// 全局测试集合 +/// +[CollectionDefinition( "Global" )] +public class GlobalCollection : ICollectionFixture { +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalFixture.cs b/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalFixture.cs new file mode 100644 index 000000000..892a6972e --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalFixture.cs @@ -0,0 +1,66 @@ +using Serilog; +using Util.Logging.Serilog; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Util.Microservices.Dapr.Tests.Fixtures; + +/// +/// 全局测试设置 +/// +public class GlobalFixture : IDisposable { + /// + /// app_webapi + /// + public const string AppId = "app_webapi"; + /// + /// Dapr Http端口 + /// + public int DaprHttpPort { get; } + + /// + /// 测试初始化 + /// + public GlobalFixture() { + ConfigLog(); + var loggerFactory = Ioc.Create(); + var log = loggerFactory.CreateLogger( "Util.Microservices.Dapr.Tests.Integration" ); + var command = ExecuteDaprRunCommand( log ); + DaprHttpPort = command.GetDaprHttpPort(); + } + + /// + /// 配置日志 + /// + private void ConfigLog() { + var services = Ioc.GetServices(); + services.AddLogging( loggingBuilder => { + Log.Logger = new LoggerConfiguration() + .WriteTo.Seq( Config.GetValue( "SeqUrl" ) ) + .MinimumLevel.Verbose() + .Enrich.FromLogContext() + .Enrich.WithLogContext() + .Enrich.WithProperty( "ApplicationName", "Util.Microservices.Dapr.Tests.Integration" ) + .CreateLogger(); + loggingBuilder.AddSerilog( Log.Logger ); + } ); + } + + /// + /// 通过命令行启动Dapr + /// + private DaprRunCommand ExecuteDaprRunCommand( ILogger log ) { + var command = DaprRunCommand.Create( AppId ); + command.Project( "../../../../../test/Util.Microservices.Dapr.WebApiSample/Util.Microservices.Dapr.WebApiSample.csproj" ) + .UseFreePorts() + .Log( log ) + .Execute(); + return command; + } + + /// + /// 测试清理 + /// + public void Dispose() { + DaprStopCommand.Create( AppId ).Execute(); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/pubsub.yaml b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/pubsub.yaml new file mode 100644 index 000000000..e3e316473 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/pubsub.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/statestore.yaml b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/statestore.yaml new file mode 100644 index 000000000..0efbca01d --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/statestore.yaml @@ -0,0 +1,13 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" + - name: actorStateStore + value: "true" diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Resources/configuration/config.yaml b/test/Util.Microservices.Dapr.Tests.Integration/Resources/configuration/config.yaml new file mode 100644 index 000000000..69b47eebc --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Resources/configuration/config.yaml @@ -0,0 +1,9 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: daprConfig +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: http://localhost:9411/api/v2/spans \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerDto.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerDto.cs new file mode 100644 index 000000000..c0ade1116 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerDto.cs @@ -0,0 +1,85 @@ +using Util.Applications.Dtos; +using Util.Domain.Biz.Enums; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 客户参数 +/// +public class CustomerDto : DtoBase { + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerQuery.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerQuery.cs new file mode 100644 index 000000000..d6b6f255c --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerQuery.cs @@ -0,0 +1,112 @@ +using Util.Data.Queries; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 客户查询参数 +/// +public class CustomerQuery : QueryParameter { + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 编码 + /// + private string _code = string.Empty; + /// + /// 编码 + /// + [Description( "编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 姓名 + /// + private string _name = string.Empty; + /// + /// 姓名 + /// + [Description( "姓名" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 昵称 + /// + private string _nickname = string.Empty; + /// + /// 昵称 + /// + [Description( "昵称" )] + public string Nickname { + get => _nickname == null ? string.Empty : _nickname.Trim(); + set => _nickname = value; + } + /// + /// 起始出生日期 + /// + public DateTime? BeginBirthday { get; set; } + /// + /// 结束出生日期 + /// + public DateTime? EndBirthday { get; set; } + /// + /// 手机号 + /// + private string _phone = string.Empty; + /// + /// 手机号 + /// + [Description( "手机号" )] + public string Phone { + get => _phone == null ? string.Empty : _phone.Trim(); + set => _phone = value; + } + /// + /// 电子邮件 + /// + private string _email = string.Empty; + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + public string Email { + get => _email == null ? string.Empty : _email.Trim(); + set => _email = value; + } + /// + /// 年龄 + /// + public int? Age { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/MockHttpContextAccessor.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/MockHttpContextAccessor.cs new file mode 100644 index 000000000..7bfe8ca9a --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/MockHttpContextAccessor.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace Util.Microservices.Dapr.Tests.Samples { + /// + /// 模拟Http上下文访问器 + /// + public class MockHttpContextAccessor : IHttpContextAccessor { + /// + /// 初始化模拟Http上下文访问器 + /// + public MockHttpContextAccessor() { + HttpContext = new DefaultHttpContext(); + HttpContext.Request.Headers["test_1"] = "test_1"; + HttpContext.Request.Headers.ContentLanguage = new StringValues( "en" ); + } + + /// + /// Http上下文 + /// + public HttpContext HttpContext { get; set; } + } +} diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductDto.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductDto.cs new file mode 100644 index 000000000..e173b5bd3 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductDto.cs @@ -0,0 +1,70 @@ +using Util.Applications.Dtos; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 产品参数 +/// +public class ProductDto : DtoBase { + /// + /// 产品编码 + /// + [Description( "产品编码" )] + [MaxLength( 50 )] + public string Code { get; set; } + /// + /// 产品名称 + /// + [Description( "产品名称" )] + [MaxLength( 500 )] + public string Name { get; set; } + /// + /// 价格 + /// + [Description( "价格" )] + public decimal Price { get; set; } + /// + /// 描述 + /// + [Description( "描述" )] + public string Description { get; set; } + /// + /// 启用 + /// + [Description( "启用" )] + public bool Enabled { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } + /// + /// 简单扩展属性 + /// + public string TestProperty1 { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductQuery.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductQuery.cs new file mode 100644 index 000000000..06a250493 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductQuery.cs @@ -0,0 +1,86 @@ +using Util.Data.Queries; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 产品查询参数 +/// +public class ProductQuery : QueryParameter { + /// + /// 产品标识 + /// + [Description( "产品标识" )] + public Guid? ProductId { get; set; } + /// + /// 产品编码 + /// + private string _code = string.Empty; + /// + /// 产品编码 + /// + [Description( "产品编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 产品名称 + /// + private string _name = string.Empty; + /// + /// 产品名称 + /// + [Description( "产品名称" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 价格 + /// + [Description( "价格" )] + public decimal? Price { get; set; } + /// + /// 描述 + /// + private string _description = string.Empty; + /// + /// 描述 + /// + [Description( "描述" )] + public string Description { + get => _description == null ? string.Empty : _description.Trim(); + set => _description = value; + } + /// + /// 启用 + /// + [Description( "启用" )] + public bool? Enabled { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/HttpClientTest.cs b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/HttpClientTest.cs new file mode 100644 index 000000000..22f14ae11 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/HttpClientTest.cs @@ -0,0 +1,73 @@ +using Util.Microservices.Dapr.Tests.Fixtures; +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.ServiceInvocations; + +/// +/// 使用Http Client调用服务测试 +/// 说明: 请求WebApi项目 Util.Microservices.Dapr.WebApiSample +/// +[Collection( "Global" )] +public class HttpClientTest { + /// + /// Http客户端操作 + /// + private readonly IHttpClient _client; + /// + /// 日志 + /// + private readonly ILogger _logger; + + /// + /// 测试初始化 + /// + public HttpClientTest( GlobalFixture fixture, IMicroserviceClientFactory factory, ILogger logger ) { + _client = factory.AppId( GlobalFixture.AppId ) + .DaprHttpPort( fixture.DaprHttpPort ) + .Create().HttpClient; + _logger = logger; + } + + /// + /// 请求Test1Controller控制器Get_1方法,返回字符串 + /// + [Fact] + public async Task Test_1() { + var result = await _client.Get( "test1" ).GetResultAsync(); + _logger.LogDebug( result ); + Assert.Equal( "ok", result ); + } + + /// + /// 请求Test1Controller控制器Get_2方法,传入id参数 + /// + [Fact] + public async Task Test_2() { + var result = await _client.Get( "test1/1" ).GetResultAsync(); + _logger.LogDebug( result ); + Assert.Equal( "id:1", result ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query方法 + /// + [Fact] + public async Task Test_3() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _client.Get>( "/api/test3/query", query ).GetResultAsync(); + Assert.NotNull( result ); + Assert.Equal( "ok", result.Data.Name ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query_Authorize方法 - 手工设置访问令牌 + /// + [Fact] + public async Task Test_4() { + var token = Config.GetValue( "BearerToken" ); + var query = new CustomerQuery { Name = "ok" }; + var result = await _client.Get>( "/api/test3/query_authorize", query ).BearerToken( token ).GetResultAsync(); + Assert.NotNull( result ); + Assert.Equal( "ok", result.Data.Name ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.NotUnPack.cs b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.NotUnPack.cs new file mode 100644 index 000000000..392aa9dc1 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.NotUnPack.cs @@ -0,0 +1,95 @@ +using Util.Exceptions; +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.ServiceInvocations; + +/// +/// 服务调用测试 - Web Api接口测试 - 不进行解包 +/// 说明: 请求项目 Util.Microservices.Dapr.WebApiSample +/// +public partial class ServiceInvocationTest { + + #region 测试无参数无返回值的场景 + + /// + /// 测试调用方法 - 请求Test2Controller控制器Get方法 - 无参数,无返回值 - 成功 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_1() { + await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2" ); + } + + /// + /// 测试调用方法 - 请求Test2Controller控制器Get_Fail方法 - 无参数,无返回值 - 失败,抛出异常 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_2() { + await Assert.ThrowsAsync( async () => { + await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2/fail" ); + } ); + } + + #endregion + + #region 测试有参数无返回值的场景 + + /// + /// 测试调用方法 - 请求Test2Controller控制器Post方法 - 有参数,无返回值 - Code参数为2,成功 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_3() { + var dto = new CustomerDto { Code = "2" }; + await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2", dto ); + } + + /// + /// 测试调用方法 - 请求Test2Controller控制器Post方法 - 有参数,无返回值 - Code参数为1,返回失败消息 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_4() { + await Assert.ThrowsAsync( async () => { + var dto = new CustomerDto { Code = "1" }; + await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2", dto ); + } ); + } + + #endregion + + #region 测试无参数有返回值的场景 + + /// + /// 测试调用方法 - 请求Test2Controller控制器Get_Customer方法 - 无参数,有返回值 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_5() { + var result = await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2/customer" ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + + #region 测试有参数有返回值的场景 + + /// + /// 测试调用方法 - 请求Test2Controller控制器Query方法 - 有参数,有返回值 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_6() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2/query", query ); + Assert.Equal( "ok", result.Name ); + } + + /// + /// 测试调用方法 - 请求Test2Controller控制器Query方法 - 有参数,有返回值 - 请求参数使用泛型 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_7() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2/query", query ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.UnPack.cs b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.UnPack.cs new file mode 100644 index 000000000..b93f2eeb3 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.UnPack.cs @@ -0,0 +1,336 @@ +using System.Collections.Generic; +using Util.Applications; +using Util.Data; +using Util.Exceptions; +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.ServiceInvocations; + +/// +/// 服务调用测试 - Web Api接口测试 - 使用ServiceResult解包 +/// 说明: 请求项目 Util.Microservices.Dapr.WebApiSample +/// +public partial class ServiceInvocationTest { + + #region 测试无参数无返回值的场景 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Get方法 - 无参数,无返回值 - 状态码为成功 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_1() { + await _serviceInvocation.InvokeAsync( "/api/Test3" ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Get_Fail方法 - 无参数,无返回值 - 状态码为失败,抛出异常 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_2() { + await Assert.ThrowsAsync( async () => { + await _serviceInvocation.InvokeAsync( "/api/Test3/fail" ); + } ); + } + + #endregion + + #region 测试有参数无返回值的场景 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Post方法 - 有参数,无返回值 - Code参数为2,返回成功 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_3() { + var dto = new CustomerDto { Code = "2" }; + await _serviceInvocation.InvokeAsync( "/api/Test3", dto ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Post方法 - 有参数,无返回值 - Code参数为1,返回失败消息 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_4() { + await Assert.ThrowsAsync( async () => { + var dto = new CustomerDto { Code = "1" }; + await _serviceInvocation.InvokeAsync( "/api/Test3", dto ); + } ); + } + + #endregion + + #region 测试无参数有返回值的场景 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Get_Customer方法 - 无参数,有返回值 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_5() { + var result = await _serviceInvocation.InvokeAsync( "/api/Test3/customer" ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + + #region 测试有参数有返回值的场景 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query方法 - 有参数,有返回值 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_6() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.InvokeAsync( "/api/Test3/query", query ); + Assert.Equal( "ok", result.Name ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query方法 - 有参数,有返回值 - 请求参数使用泛型 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_7() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.InvokeAsync( "/api/Test3/query", query ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + + #region 请求地址处理 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query方法 - 请求地址处理,Test3/query处理为 /api/Test3/query + /// + [Fact] + public async Task TestInvokeAsync_Unpack_8() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.InvokeAsync( "Test3/query", query ); + Assert.Equal( "ok", result.Name ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query2方法 - 请求地址以/开头,不进行处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_9() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.InvokeAsync( "/query", query ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + + #region 请求头处理 + + /// + /// 测试设置请求头 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Header_1() { + var query = new CustomerQuery(); + var result = await _serviceInvocation.Header( "CustomerName", "abc" ).InvokeAsync( "test3/query_header", query ); + Assert.Equal( "abc", result.Name ); + } + + /// + /// 测试设置请求头集合 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Header_2() { + var query = new CustomerQuery(); + var result = await _serviceInvocation.Header( new Dictionary { + {"CustomerCode","a"}, + {"CustomerName","b"} + } ).InvokeAsync( "test3/query_header", query ); + Assert.Equal( "a", result.Code ); + Assert.Equal( "b", result.Name ); + } + + /// + /// 测试移除请求头 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Header_3() { + var query = new CustomerQuery(); + var result = await _serviceInvocation.Header( new Dictionary { + {"CustomerCode","a"}, + {"CustomerName","b"} + } ) + .RemoveHeader( "CustomerCode" ) + .InvokeAsync( "test3/query_header", query ); + Assert.Null( result.Code ); + Assert.Equal( "b", result.Name ); + } + + /// + /// 测试移除请求头 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Header_4() { + Util.Helpers.Web.HttpContextAccessor = new MockHttpContextAccessor(); + var query = new CustomerQuery(); + var result = await _serviceInvocation + .ImportHeader( "test_1" ) + .InvokeAsync( "test3/query_header_import", query ); + Assert.Equal( "test_1", result.Code ); + } + + /// + /// 测试导入内容头 - Content-Language + /// + [Fact] + public async Task TestInvokeAsync_Unpack_ImportHeader_1() { + Util.Helpers.Web.HttpContextAccessor = new MockHttpContextAccessor(); + var query = new CustomerQuery(); + var result = await _serviceInvocation.ImportHeader( "Content-Language" ) + .InvokeAsync( "test3/query_header_import", query ); + Assert.Equal( "en", result.Name ); + } + + #endregion + + #region 身份认证处理 + + /// + /// 测试身份认证 - 请求Test3Controller控制器Query_Authorize方法 - 未授权 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Auth_1() { + var query = new CustomerQuery { Name = "ok" }; + try { + await _serviceInvocation.InvokeAsync( "test3/query_authorize", query ); + } + catch ( Warning exception ) { + Assert.Equal( StateCode.Unauthorized, exception.Code ); + } + } + + /// + /// 测试身份认证 - 请求Test3Controller控制器Query_Authorize方法 - 传递token授权 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Auth_2() { + var token = Config.GetValue( "BearerToken" ); + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.BearerToken( token ).InvokeAsync( "test3/query_authorize", query ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + + #region 测试事件处理 + + /// + /// 测试状态转换事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnState() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnState( ( code ) => ServiceState.Fail ) + .OnFail( async ( request, response, ex ) => { + query.Phone = "123"; + await Task.CompletedTask; + } ) + .InvokeAsync( "Test3/query", query ); + Assert.Equal( "123", query.Phone ); + } + + /// + /// 测试调用前事件处理 - 返回false,不执行 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnBefore() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.OnBefore( request => false ).InvokeAsync( "Test3/query", query ); + Assert.Null( result ); + } + + /// + /// 测试转换结果事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnResult() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnResult( async ( response,options,token ) => { + var json = await response.Content.ReadAsStringAsync( token ); + var result = Json.ToObject>( json, options ); + result.Data.Phone = "123"; + return result; + } ) + .InvokeAsync( "Test3/query", query ); + Assert.NotNull( result ); + Assert.Equal( "ok", result.Name ); + Assert.Equal( "123", result.Phone ); + } + + /// + /// 测试调用成功事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnSuccess() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnSuccess( async ( request, response, result ) => { + result.Phone = "123"; + await Task.CompletedTask; + } ) + .InvokeAsync( "Test3/query", query ); + Assert.NotNull( result ); + Assert.Equal( "ok", result.Name ); + Assert.Equal( "123", result.Phone ); + } + + /// + /// 测试失败事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnFail() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnFail( async ( request, response, ex ) => { + query.Phone = "123"; + await Task.CompletedTask; + } ) + .InvokeAsync( "Test3/fail", query ); + Assert.Equal( "123", query.Phone ); + } + + /// + /// 测试未授权事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnUnauthorized() { + var query = new CustomerQuery { Name = "ok" }; + try { + await _serviceInvocation.OnUnauthorized( async ( request, response ) => { + query.Phone = "123"; + await Task.CompletedTask; + } ) + .InvokeAsync( "test3/query_authorize", query ); + } + catch { + Assert.Equal( "123", query.Phone ); + } + } + + /// + /// 测试完成事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnComplete() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnComplete( async ( request, response ) => { + query.Phone = "123"; + await Task.CompletedTask; + } ) + .InvokeAsync>( "Test3/page_query", query ); + Assert.NotNull( result ); + Assert.Equal( 50, result.Total ); + Assert.Equal( "ok", result.Data[0].Name ); + Assert.Equal( "123", query.Phone ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.cs b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.cs new file mode 100644 index 000000000..2473e8a65 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.cs @@ -0,0 +1,30 @@ +using Util.Microservices.Dapr.Tests.Fixtures; + +namespace Util.Microservices.Dapr.Tests.ServiceInvocations; + +/// +/// 服务调用测试 - Web Api接口测试 +/// 说明: 请求项目 Util.Microservices.Dapr.WebApiSample +/// +[Collection( "Global" )] +public partial class ServiceInvocationTest { + /// + /// 服务调用操作 + /// + private readonly IServiceInvocation _serviceInvocation; + /// + /// 日志 + /// + private readonly ILogger _logger; + + /// + /// 测试初始化 + /// + public ServiceInvocationTest( GlobalFixture fixture, IMicroserviceClientFactory factory, ILogger logger ) { + _serviceInvocation = factory.AppId( GlobalFixture.AppId ) + .DaprHttpPort( fixture.DaprHttpPort ) + .Create() + .ServiceInvocation; + _logger = logger; + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Startup.cs b/test/Util.Microservices.Dapr.Tests.Integration/Startup.cs new file mode 100644 index 000000000..49b5978d3 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Startup.cs @@ -0,0 +1,28 @@ +using Util.Logging.Serilog; + +namespace Util.Microservices.Dapr.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Util.Helpers.Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddSerilog( "Util.Microservices.Dapr.Tests.Integration" ) + .AddDapr() + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + Ioc.SetServiceProviderAction( services.BuildServiceProvider ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Usings.cs b/test/Util.Microservices.Dapr.Tests.Integration/Usings.cs new file mode 100644 index 000000000..67be8598d --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Usings.cs @@ -0,0 +1,15 @@ +global using System; +global using System.Net.Http; +global using Xunit; +global using Xunit.Abstractions; +global using Xunit.DependencyInjection.Logging; +global using System.Text; +global using System.Threading.Tasks; +global using System.ComponentModel; +global using System.ComponentModel.DataAnnotations; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Util.Helpers; +global using Util.Microservices.Dapr.CommandLines; +global using Util.Http; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Util.Microservices.Dapr.Tests.Integration.csproj b/test/Util.Microservices.Dapr.Tests.Integration/Util.Microservices.Dapr.Tests.Integration.csproj new file mode 100644 index 000000000..eb3465160 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Util.Microservices.Dapr.Tests.Integration.csproj @@ -0,0 +1,52 @@ + + + + $(NetTargetFramework) + false + Util.Microservices.Dapr.Tests + Util.Microservices.Dapr.Tests.Startup + + + + + + + + + Always + true + PreserveNewest + + + Always + true + PreserveNewest + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + diff --git a/test/Util.Microservices.Dapr.Tests.Integration/appsettings.Development.json b/test/Util.Microservices.Dapr.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..be333d2db --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/appsettings.Development.json @@ -0,0 +1,20 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "Serilog": { + "WriteTo": [ + { + "Name": "Seq", + "Args": { + "serverUrl": "http://localhost:5380" + } + } + ] + }, + "SeqUrl": "http://localhost:5380", + "BearerToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkQxODgyOUQyNzdGRjRGNjA4MjM2QTI0ODVDM0QwNUFGIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2OTA2MTE0OTAsImV4cCI6MTkwMDYxMTQ5MCwiaXNzIjoiaHR0cHM6Ly93d3cuYS5jb20vaWRlbnRpdHkiLCJjbGllbnRfaWQiOiJhZG1pbi11aSIsInN1YiI6IjU1YmE1M2E2LWU0ODItNGQ5Yi04ZDkxLTNmYmE2NjEwYjg5NiIsImF1dGhfdGltZSI6MTY5MDYxMTQ4NiwiaWRwIjoibG9jYWwiLCJBc3BOZXQuSWRlbnRpdHkuU2VjdXJpdHlTdGFtcCI6IkJCN1NaTllXWTMzM1I3QVVUUVZaUUtWMlVJQVRRSk1IIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWRtaW4iLCJuYW1lIjoiYWRtaW4iLCJhcHBsaWNhdGlvbl9pZCI6ImU5MTM4YTM1LWE0ZmYtNDYwZS1hYzU1LWI3NDNkNTViOTY5MSIsImFwcGxpY2F0aW9uX2NvZGUiOiJhZG1pbi11aSIsImFwcGxpY2F0aW9uX25hbWUiOiLnrqHnkIbns7vnu5_lkI7lj7AiLCJqdGkiOiI2MDhDNDFDRkJBODg1RTI1QTMyRUM1MDc4NjQyQjYwMyIsInNpZCI6IjI5RkE0ODQwQTRBRDVDNkVFMDA2NzhFQTU2MUQ0Mzk3IiwiaWF0IjoxNjkwNjExNDkwLCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbInB3ZCJdfQ.Shj4kaWH0G6j13jtoMkAZo03Y9WGDPSORHh39U2707EFMKmAekumy6YNIceGWR6lXmP0mzJEHfFUwKP3hAwa91boccIyLwNwLoq0fQIst-94zU5es7sdC_o38s7AJh0U6z4H0hTgsaDBddnmSb4atVC18-XJnqHiMeKJdmQYuIcVBe6mLzzexeW6ZCjXUCb0MI0z2mNVJqd_f99Nk9kgxoYrKc8yjDBiWyYsWdfgvbly4kmjub17wWFXrEdkrq1ldshFqKVdk3urKs0N5L9QeHHht7oYyiLpES49PJBk8Qn2ozDvmF49FFwsiT3kd8LYBF5LNhSve0X2ANqcnRojiA" +} diff --git a/test/Util.Microservices.Dapr.Tests.Integration/appsettings.json b/test/Util.Microservices.Dapr.Tests.Integration/appsettings.json new file mode 100644 index 000000000..03565085b --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/clear.ps1 b/test/Util.Microservices.Dapr.Tests.Integration/clear.ps1 new file mode 100644 index 000000000..fc85683c8 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/clear.ps1 @@ -0,0 +1,6 @@ +Write-Host "Stop-Process -Name Util.Microservices.Dapr*" +Stop-Process -Name "Util.Microservices.Dapr*" +Write-Host "Stop-Process -Name dapr*" +Stop-Process -Name "dapr*" +Write-Host "Stop-Process -Name dotnet*" +Stop-Process -Name "dotnet*" \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Authorization/PermissionManager.cs b/test/Util.Microservices.Dapr.WebApiSample/Authorization/PermissionManager.cs new file mode 100644 index 000000000..bbc2c005c --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Authorization/PermissionManager.cs @@ -0,0 +1,16 @@ +using Util.Security.Authorization; + +namespace Util.Microservices.Dapr.WebApiSample.Authorization; + +/// +/// 权限管理器 +/// +public class PermissionManager : IPermissionManager { + /// + /// 是否有权限访问 + /// + /// 资源标识 + public Task HasPermissionAsync( string resourceUri ) { + return Task.FromResult( false ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test1Controller.cs b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test1Controller.cs new file mode 100644 index 000000000..fdef22450 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test1Controller.cs @@ -0,0 +1,24 @@ +namespace Util.Microservices.Dapr.WebApiSample.Controllers; + +/// +/// 1 - ڲʹHttpClientȡַ +/// +[ApiController] +[Route("Test1")] +public class Test1Controller : ControllerBase { + /// + /// HttpGet,ַ + /// + [HttpGet] + public string Get_1() { + return "ok"; + } + + /// + /// HttpGet, + /// + [HttpGet( "{id}" )] + public string Get_2( string id ) { + return $"id:{id}"; + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test2Controller.cs b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test2Controller.cs new file mode 100644 index 000000000..768c7f8c6 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test2Controller.cs @@ -0,0 +1,57 @@ +using Util.Exceptions; +using Util.Microservices.Dapr.WebApiSample.Dtos; + +namespace Util.Microservices.Dapr.WebApiSample.Controllers { + /// + /// 2 - ڲʹ÷ûȡδװԭʼ + /// + [ApiController] + [Route("Test2")] + public class Test2Controller : ControllerBase { + /// + /// Get - ޲ - ޷ֵ - ɹ + /// + [HttpGet] + public void Get() { + } + + /// + /// Get - ޲ - ޷ֵ - ʧ + /// + [HttpGet( "fail" )] + public IActionResult Get_Fail() { + return new BadRequestResult(); + } + + /// + /// Post - в - ޷ֵ + /// + [HttpPost] + public void Post( CustomerDto dto ) { + if ( dto.Code != "2" ) + throw new Warning( "idΪ2" ); + } + + /// + /// Get - ޲ - зֵ + /// + [HttpGet( "customer" )] + public CustomerDto Get_Customer() { + var dto = new CustomerDto { + Name = "ok" + }; + return dto; + } + + /// + /// Get - в - зֵ + /// + [HttpGet( "query" )] + public CustomerDto Query( [FromQuery] CustomerQuery query ) { + var dto = new CustomerDto { + Name = query.Name + }; + return dto; + } + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test3Controller.cs b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test3Controller.cs new file mode 100644 index 000000000..4618b8c56 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test3Controller.cs @@ -0,0 +1,125 @@ +using Microsoft.AspNetCore.Authorization; +using Util.Applications.Controllers; +using Util.Data; +using Util.Exceptions; +using Util.Microservices.Dapr.WebApiSample.Dtos; + +namespace Util.Microservices.Dapr.WebApiSample.Controllers; + +/// +/// 测试3控制器 - 用于测试包装结果对象 +/// +public class Test3Controller : WebApiControllerBase { + /// + /// 测试Get方法访问 - 无参数 - 无返回值 - 成功 + /// + [HttpGet] + public IActionResult Get() { + return Success(); + } + + /// + /// 测试Get方法访问 - 无参数 - 无返回值 - 失败 + /// + [HttpGet("fail")] + public IActionResult Get_Fail() { + return Fail( "fail" ); + } + + /// + /// 测试Post方法访问 - 有参数 - 无返回值 + /// + [HttpPost] + public IActionResult Post( CustomerDto dto ) { + if ( dto.Code != "2" ) + throw new Warning( "id必须为2" ); + return Success(); + } + + /// + /// 测试Get方法访问 - 无参数 - 有返回值 + /// + [HttpGet( "customer" )] + public IActionResult Get_Customer() { + var dto = new CustomerDto { + Name = "ok" + }; + return Success( dto ); + } + + /// + /// 测试Get方法访问 - 有参数 - 有返回值 + /// + [HttpGet( "query" )] + public IActionResult Query( [FromQuery] CustomerQuery query ) { + var dto = new CustomerDto { + Name = query.Name + }; + return Success( dto ); + } + + /// + /// 测试自定义地址 + /// + [HttpGet( "/query" )] + public IActionResult Query2( [FromQuery] CustomerQuery query ) { + var dto = new CustomerDto { + Name = query.Name + }; + return Success( dto ); + } + + /// + /// 测试需要身份认证 + /// + [HttpGet( "query_authorize" )] + [Authorize] + public IActionResult Query_Authorize( [FromQuery] CustomerQuery query ) { + var dto = new CustomerDto { + Name = query.Name + }; + return Success( dto ); + } + + /// + /// 测试分页对象 + /// + [HttpGet( "page_query" )] + public IActionResult PageQuery( [FromQuery] CustomerQuery query ) { + PageList result = new PageList(); + result.Total = 50; + result.Data.Add( new CustomerDto { + Name = query.Name + } ); + return Success( result ); + } + + /// + /// 测试添加请求头 + /// + [HttpGet( "query_header" )] + public IActionResult Query2_Header_1( [FromQuery] CustomerQuery query ) { + var customerCode = Request.Headers["CustomerCode"].FirstOrDefault(); + var customerName = Request.Headers["CustomerName"].FirstOrDefault(); + var test_1 = Request.Headers["test_1"].FirstOrDefault(); + var dto = new CustomerDto { + Code = customerCode, + Name = customerName + }; + return Success( dto ); + } + + /// + /// 测试导入请求头 + /// + [HttpGet( "query_header_import" )] + public IActionResult Query2_Header_2( [FromQuery] CustomerQuery query ) { + var test_1 = Request.Headers["test_1"].FirstOrDefault(); + var contentLanguage = Request.Headers.ContentLanguage.FirstOrDefault(); + var dto = new CustomerDto { + Code = test_1, + Name = contentLanguage + }; + return Success( dto ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerDto.cs b/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerDto.cs new file mode 100644 index 000000000..09eca5a44 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerDto.cs @@ -0,0 +1,76 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Applications.Dtos; + +namespace Util.Microservices.Dapr.WebApiSample.Dtos { + /// + /// 客户参数 + /// + public class CustomerDto : DtoBase { + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerQuery.cs b/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerQuery.cs new file mode 100644 index 000000000..e7124fcd3 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerQuery.cs @@ -0,0 +1,113 @@ +using System.ComponentModel; +using Util.Data.Queries; + +namespace Util.Microservices.Dapr.WebApiSample.Dtos; + +/// +/// 客户查询参数 +/// +public class CustomerQuery : QueryParameter { + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 编码 + /// + private string _code = string.Empty; + /// + /// 编码 + /// + [Description( "编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 姓名 + /// + private string _name = string.Empty; + /// + /// 姓名 + /// + [Description( "姓名" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 昵称 + /// + private string _nickname = string.Empty; + /// + /// 昵称 + /// + [Description( "昵称" )] + public string Nickname { + get => _nickname == null ? string.Empty : _nickname.Trim(); + set => _nickname = value; + } + /// + /// 起始出生日期 + /// + public DateTime? BeginBirthday { get; set; } + /// + /// 结束出生日期 + /// + public DateTime? EndBirthday { get; set; } + /// + /// 手机号 + /// + private string _phone = string.Empty; + /// + /// 手机号 + /// + [Description( "手机号" )] + public string Phone { + get => _phone == null ? string.Empty : _phone.Trim(); + set => _phone = value; + } + /// + /// 电子邮件 + /// + private string _email = string.Empty; + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + public string Email { + get => _email == null ? string.Empty : _email.Trim(); + set => _email = value; + } + /// + /// 年龄 + /// + public int? Age { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Program.cs b/test/Util.Microservices.Dapr.WebApiSample/Program.cs new file mode 100644 index 000000000..01a86346b --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Program.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Util.Logging.Serilog; +using Util.Microservices.Dapr.WebApiSample.Authorization; + +var builder = WebApplication.CreateBuilder( args ); + +builder.Services.AddControllers(); +builder.Services.AddAuthentication( JwtBearerDefaults.AuthenticationScheme ) + .AddJwtBearer( options => { + options.Authority = builder.Configuration.GetValue( "IdentityUrl" ); + options.RequireHttpsMetadata = false; + options.TokenValidationParameters.ValidateAudience = false; + options.TokenValidationParameters.ValidateIssuer = false; + } ); +builder.AsBuild() + .AddAcl() + .AddSerilog( "Util.Microservices.Dapr.WebApiSample" ) + .AddUtil(); + +var app = builder.Build(); +app.UseAuthentication(); +app.UseAuthorization(); +app.MapControllers(); +app.Run(); diff --git a/test/Util.Microservices.Dapr.WebApiSample/Properties/launchSettings.json b/test/Util.Microservices.Dapr.WebApiSample/Properties/launchSettings.json new file mode 100644 index 000000000..6cb703893 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:45513", + "sslPort": 44312 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5031", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7258;http://localhost:5031", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/Util.Microservices.Dapr.WebApiSample/Usings.cs b/test/Util.Microservices.Dapr.WebApiSample/Usings.cs new file mode 100644 index 000000000..85103500f --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Usings.cs @@ -0,0 +1,10 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.AspNetCore.Mvc; +global using Util; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Util.Microservices.Dapr.WebApiSample.csproj b/test/Util.Microservices.Dapr.WebApiSample/Util.Microservices.Dapr.WebApiSample.csproj new file mode 100644 index 000000000..4e544103f --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Util.Microservices.Dapr.WebApiSample.csproj @@ -0,0 +1,17 @@ + + + + $(NetTargetFramework) + enable + + + + + + + + + + + + diff --git a/test/Util.Microservices.Dapr.WebApiSample/appsettings.Development.json b/test/Util.Microservices.Dapr.WebApiSample/appsettings.Development.json new file mode 100644 index 000000000..8658b8474 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/appsettings.Development.json @@ -0,0 +1,22 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft.AspNetCore": "Trace" + } + }, + "Serilog": { + "WriteTo": [ + { + "Name": "Seq", + "Args": { + "serverUrl": "http://localhost:5380" + } + } + ] + }, + "ConnectionStrings": { + "connection": "Server=.\\sql2019;Database=Util.WebApi.Test;uid=sa;pwd=sa;MultipleActiveResultSets=true;TrustServerCertificate=true" + }, + "IdentityUrl": "http://localhost:30100" +} diff --git a/test/Util.Microservices.Dapr.WebApiSample/appsettings.json b/test/Util.Microservices.Dapr.WebApiSample/appsettings.json new file mode 100644 index 000000000..a531fb681 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft.AspNetCore": "Trace" + } + }, + "AllowedHosts": "*" +} diff --git a/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.Async.cs b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.Async.cs new file mode 100644 index 000000000..8bc3f1ff2 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.Async.cs @@ -0,0 +1,310 @@ +using Util.Microservices.Polly.Tests.Samples; + +namespace Util.Microservices.Polly.Tests; + +/// +/// ԴԲ - Բ - 첽 +/// +public partial class PolicyTest { + + #region HandleException + + /// + /// - 쳣 - 1 + /// + [Fact] + public async Task TestRetry_1_Async() { + var i = 0; + await Assert.ThrowsAsync( async () => { + await _policy.Retry().HandleException().ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + Assert.Equal( 2, i ); + } + + /// + /// - 쳣 - 3 + /// + [Fact] + public async Task TestRetry_2_Async() { + var i = 0; + await Assert.ThrowsAsync( async () => { + await _policy.Retry( 3 ).HandleException().ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + Assert.Equal( 4, i ); + } + + /// + /// - 쳣 - + /// + [Fact] + public async Task TestRetry_3_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + await _policy.Retry().HandleException().Forever().ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + if ( i < 100 ) + throw new Exception(); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 100, i ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - 쳣 - 3 - ȴ + /// + [Fact] + public async Task TestRetry_4_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + await Assert.ThrowsAsync( async () => { + await _policy.Retry( 3 ).HandleException() + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 4, i ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + /// + /// - 쳣 - ص + /// + [Fact] + public async Task TestRetry_5_Async() { + var i = 0; + await Assert.ThrowsAsync( async () => { + await _policy.Retry( 3 ).HandleException() + .OnRetry( ( e, n ) => { i = n; } ) + .ExecuteAsync( async () => { + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - + /// + [Fact] + public async Task TestRetry_6_Async() { + var i = 0; + await _policy.Retry().HandleException().Forever() + .OnRetry( ( e, n ) => { i = n; } ) + .ExecuteAsync( async () => { + await Task.CompletedTask; + if ( i < 3 ) + throw new Exception(); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - ȴ + /// + [Fact] + public async Task TestRetry_7_Async() { + var i = 0; + await Assert.ThrowsAsync( async () => { + await _policy.Retry( 3 ).HandleException() + .Wait( retry => TimeSpan.FromMilliseconds( 1 ) ) + .OnRetry( ( e, n ) => { i = n; } ) + .ExecuteAsync( async () => { + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - ȴ - + /// + [Fact] + public async Task TestRetry_8_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + await _policy.Retry().HandleException().Forever() + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .OnRetry( ( e, n ) => { i = n; } ) + .ExecuteAsync( async () => { + await Task.CompletedTask; + if ( i < 3 ) + throw new Exception(); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 3, i ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + #endregion + + #region HandleResult + + /// + /// - - 1 + /// + [Fact] + public async Task TestRetry_HandleResult_1_Async() { + var i = 0; + var result = await _policy.Retry().HandleResult( t => t.Result != "ok" ) + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + return new SampleResult { Result = i.ToString() }; + } ); + Assert.Equal( "2", result.Result ); + } + + /// + /// - - 3 + /// + [Fact] + public async Task TestRetry_HandleResult_2_Async() { + var i = 0; + var result = await _policy.Retry( 3 ).HandleResult( t => t.Result != "ok" ) + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + return new SampleResult { Result = i.ToString() }; + } ); + Assert.Equal( "4", result.Result ); + } + + /// + /// - - + /// + [Fact] + public async Task TestRetry_HandleResult_3_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = await _policy.Retry() + .HandleResult( t => t.Result != "ok" ) + .Forever() + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + if ( i < 100 ) + return new SampleResult { Result = i.ToString() }; + return new SampleResult { Result = "ok" }; + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "ok", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - - 3 - ȴ + /// + [Fact] + public async Task TestRetry_HandleResult_4_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = await _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + return new SampleResult { Result = i.ToString() }; + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "4", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + /// + /// - - ص + /// + [Fact] + public async Task TestRetry_HandleResult_5_Async() { + var sample = new SampleResult(); + var result = await _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .ExecuteAsync( async () => await Task.FromResult(sample) ); + Assert.Equal( "3", result.Result ); + } + + /// + /// - - ص - + /// + [Fact] + public async Task TestRetry_HandleResult_6_Async() { + var sample = new SampleResult(); + var result = await _policy.Retry() + .HandleResult( t => t.Result != "10" ) + .Forever() + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .ExecuteAsync( async () => await Task.FromResult( sample ) ); + Assert.Equal( "10", result.Result ); + } + + /// + /// - - ص - ȴ + /// + [Fact] + public async Task TestRetry_HandleResult_7_Async() { + var sample = new SampleResult(); + var result = await _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .Wait( retry => TimeSpan.FromMilliseconds( 1 ) ) + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .ExecuteAsync( async () => await Task.FromResult( sample ) ); + Assert.Equal( "3", result.Result ); + } + + /// + /// - - ص - ȴ - + /// + [Fact] + public async Task TestRetry_HandleResult_8_Async() { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var sample = new SampleResult(); + var result = await _policy.Retry() + .HandleResult( t => t.Result != "3" ) + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .Forever() + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .ExecuteAsync( async () => await Task.FromResult( sample ) ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "3", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.cs b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.cs new file mode 100644 index 000000000..9c741b148 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.cs @@ -0,0 +1,307 @@ +using Util.Microservices.Polly.Tests.Samples; + +namespace Util.Microservices.Polly.Tests; + +/// +/// ԴԲ - Բ +/// +public partial class PolicyTest { + + #region HandleException + + /// + /// - 쳣 - 1 + /// + [Fact] + public void TestRetry_1() { + var i = 0; + Assert.Throws( () => { + _policy.Retry().HandleException().Execute( () => { + i++; + throw new Exception(); + } ); + } ); + Assert.Equal( 2, i ); + } + + /// + /// - 쳣 - 3 + /// + [Fact] + public void TestRetry_2() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + Assert.Throws( () => { + _policy.Retry( 3 ).HandleException().Execute( () => { + i++; + throw new Exception(); + } ); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 4, i ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - 쳣 - + /// + [Fact] + public void TestRetry_3() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + _policy.Retry().HandleException().Forever().Execute( () => { + i++; + if ( i < 100 ) + throw new Exception(); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 100, i ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - 쳣 - 3 - 1εȴ10룬2εȴ20룬3εȴ30 + /// + [Fact] + public void TestRetry_4() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + Assert.Throws( () => { + _policy.Retry( 3 ).HandleException() + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .Execute( () => { + i++; + throw new Exception(); + } ); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 4, i ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + /// + /// - 쳣 - ص + /// + [Fact] + public void TestRetry_5() { + var i = 0; + Assert.Throws( () => { + _policy.Retry( 3 ).HandleException() + .OnRetry( ( e, n ) => { + i = n; + } ) + .Execute( () => throw new Exception() ); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - + /// + [Fact] + public void TestRetry_6() { + var i = 0; + _policy.Retry().HandleException().Forever() + .OnRetry( ( e, n ) => { + i = n; + } ) + .Execute( () => { + if ( i < 3 ) + throw new Exception(); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - ȴ + /// + [Fact] + public void TestRetry_7() { + var i = 0; + Assert.Throws( () => { + _policy.Retry( 3 ).HandleException() + .Wait( retry => TimeSpan.FromMilliseconds( 1 ) ) + .OnRetry( ( e, n ) => { + i = n; + } ) + .Execute( () => throw new Exception() ); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - ȴ - + /// + [Fact] + public void TestRetry_8() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + _policy.Retry().HandleException().Forever() + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .OnRetry( ( e, n ) => { + i = n; + } ) + .Execute( () => { + if ( i < 3 ) + throw new Exception(); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 3, i ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + #endregion + + #region HandleResult + + /// + /// - - 1 + /// + [Fact] + public void TestRetry_HandleResult_1() { + var i = 0; + var result = _policy.Retry().HandleResult( t => t.Result != "ok" ) + .Execute( () => { + i++; + return new SampleResult { Result = i.ToString() }; + } ); + Assert.Equal( "2", result.Result ); + } + + /// + /// - - 3 + /// + [Fact] + public void TestRetry_HandleResult_2() { + var i = 0; + var result = _policy.Retry( 3 ).HandleResult( t => t.Result != "ok" ) + .Execute( () => { + i++; + return new SampleResult { Result = i.ToString() }; + } ); + Assert.Equal( "4", result.Result ); + } + + /// + /// - - + /// + [Fact] + public void TestRetry_HandleResult_3() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = _policy.Retry() + .HandleResult( t => t.Result != "ok" ) + .Forever() + .Execute( () => { + i++; + if ( i < 100 ) + return new SampleResult { Result = i.ToString() }; + return new SampleResult { Result = "ok" }; + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "ok", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - - 3 - ȴ + /// + [Fact] + public void TestRetry_HandleResult_4() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .Execute( () => { + i++; + return new SampleResult { Result = i.ToString() }; + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "4", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + /// + /// - - ص + /// + [Fact] + public void TestRetry_HandleResult_5() { + var sample = new SampleResult(); + var result = _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .Execute( () => sample ); + Assert.Equal( "3", result.Result ); + } + + /// + /// - - ص - + /// + [Fact] + public void TestRetry_HandleResult_6() { + var sample = new SampleResult(); + var result = _policy.Retry() + .HandleResult( t => t.Result != "10" ) + .Forever() + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .Execute( () => sample ); + Assert.Equal( "10", result.Result ); + } + + /// + /// - - ص - ȴ + /// + [Fact] + public void TestRetry_HandleResult_7() { + var sample = new SampleResult(); + var result = _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .Wait( retry => TimeSpan.FromMilliseconds( 1 ) ) + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .Execute( () => sample ); + Assert.Equal( "3", result.Result ); + } + + /// + /// - - ص - ȴ - + /// + [Fact] + public void TestRetry_HandleResult_8() { + var sample = new SampleResult(); + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = _policy.Retry() + .HandleResult( t => t.Result != "3" ) + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .Forever() + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .Execute( () => sample ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "3", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.cs b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.cs new file mode 100644 index 000000000..7785844c4 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.cs @@ -0,0 +1,23 @@ +namespace Util.Microservices.Polly.Tests; + +/// +/// ԴԲ +/// +public partial class PolicyTest { + /// + /// Դ + /// + private readonly IPolicy _policy; + /// + /// + /// + private readonly ITestOutputHelper _output; + + /// + /// Գʼ + /// + public PolicyTest( IPolicy policy, ITestOutputHelper output ) { + _policy = policy; + _output = output; + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleException.cs b/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleException.cs new file mode 100644 index 000000000..f09abf6a2 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleException.cs @@ -0,0 +1,7 @@ +namespace Util.Microservices.Polly.Tests.Samples; + +/// +/// 样例异常 +/// +public class SampleException : Exception { +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleResult.cs b/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleResult.cs new file mode 100644 index 000000000..ade9d1ec4 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleResult.cs @@ -0,0 +1,11 @@ +namespace Util.Microservices.Polly.Tests.Samples; + +/// +/// 样例结果 +/// +public class SampleResult { + /// + /// 结果 + /// + public string Result { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/Startup.cs b/test/Util.Microservices.Polly.Tests.Integration/Startup.cs new file mode 100644 index 000000000..2d35ec1f5 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Startup.cs @@ -0,0 +1,22 @@ +namespace Util.Microservices.Polly.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + Ioc.SetServiceProviderAction( services.BuildServiceProvider ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/Usings.cs b/test/Util.Microservices.Polly.Tests.Integration/Usings.cs new file mode 100644 index 000000000..7fe41f2cb --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Usings.cs @@ -0,0 +1,14 @@ +global using System; +global using System.Net.Http; +global using Xunit; +global using Xunit.Abstractions; +global using Xunit.DependencyInjection.Logging; +global using System.Text; +global using System.Threading.Tasks; +global using System.Diagnostics; +global using System.ComponentModel; +global using System.ComponentModel.DataAnnotations; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Util.Helpers; diff --git a/test/Util.Microservices.Polly.Tests.Integration/Util.Microservices.Polly.Tests.Integration.csproj b/test/Util.Microservices.Polly.Tests.Integration/Util.Microservices.Polly.Tests.Integration.csproj new file mode 100644 index 000000000..30dd58c9b --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Util.Microservices.Polly.Tests.Integration.csproj @@ -0,0 +1,29 @@ + + + + $(NetTargetFramework) + false + Util.Microservices.Polly.Tests + Util.Microservices.Polly.Tests.Startup + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/Util.Scheduling.Hangfire.Tests.Integration.csproj b/test/Util.Scheduling.Hangfire.Tests.Integration/Util.Scheduling.Hangfire.Tests.Integration.csproj index 4bd11136f..bef811322 100644 --- a/test/Util.Scheduling.Hangfire.Tests.Integration/Util.Scheduling.Hangfire.Tests.Integration.csproj +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/Util.Scheduling.Hangfire.Tests.Integration.csproj @@ -10,7 +10,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -27,7 +27,7 @@ - + diff --git a/test/Util.Scheduling.Quartz.Tests.Integration/Util.Scheduling.Quartz.Tests.Integration.csproj b/test/Util.Scheduling.Quartz.Tests.Integration/Util.Scheduling.Quartz.Tests.Integration.csproj index 9f3a69bfa..c805d0aef 100644 --- a/test/Util.Scheduling.Quartz.Tests.Integration/Util.Scheduling.Quartz.Tests.Integration.csproj +++ b/test/Util.Scheduling.Quartz.Tests.Integration/Util.Scheduling.Quartz.Tests.Integration.csproj @@ -24,7 +24,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Util.TestShare/Events/TestEvent.cs b/test/Util.TestShare/Events/TestEvent.cs index 83ce068fb..c6d031078 100644 --- a/test/Util.TestShare/Events/TestEvent.cs +++ b/test/Util.TestShare/Events/TestEvent.cs @@ -1,4 +1,5 @@ -using Util.Events; +using System; +using Util.Events; namespace Util.Tests.Events; @@ -6,5 +7,6 @@ namespace Util.Tests.Events; /// 测试事件 /// public class TestEvent : IEvent { + public Guid Id { get; set; } public string Name { get; set; } } \ No newline at end of file diff --git a/test/Util.TestShare/Queries/CustomerQuery.cs b/test/Util.TestShare/Queries/CustomerQuery.cs index dad09252d..cb343080a 100644 --- a/test/Util.TestShare/Queries/CustomerQuery.cs +++ b/test/Util.TestShare/Queries/CustomerQuery.cs @@ -93,6 +93,10 @@ public string Email { set => _email = value; } /// + /// 年龄 + /// + public int? Age { get; set; } + /// /// 起始创建时间 /// public DateTime? BeginCreationTime { get; set; } diff --git a/test/Util.Ui.NgZorro.Tests/Data/TreeResultTest.cs b/test/Util.Ui.NgZorro.Tests/Data/TreeResultTest.cs index ae2d8246a..6eaf93eb0 100644 --- a/test/Util.Ui.NgZorro.Tests/Data/TreeResultTest.cs +++ b/test/Util.Ui.NgZorro.Tests/Data/TreeResultTest.cs @@ -304,6 +304,18 @@ public void TestGetResult_5() { Assert.True( child3.IsLeaf ); } + /// + /// 测试删除原始节点的子节点列表 + /// + [Fact] + public void TestGetResult_6() { + var list = new List { GetNode1(), GetNode2(), GetNode3(), GetNode4(), GetNode5() }; + var result = list.ToTreeResult(); + var root = result.Nodes.First(); + Assert.Null( root.GetOriginalNode().Children ); + Assert.True( root.Children?.Count > 0 ); + } + /// /// 测试复选框选中节点的标识列表 /// diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Base.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Base.cs index df422ceab..6fab84999 100644 --- a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Base.cs +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Base.cs @@ -150,17 +150,6 @@ public void TestBindFormControlName() { result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } - - /// - /// 测试模型变更事件 - /// - [Fact] - public void TestOnModelChange() { - _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); - var result = new StringBuilder(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } } } diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Events.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Events.cs new file mode 100644 index 000000000..4f3bd629e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Events.cs @@ -0,0 +1,44 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 事件测试 + /// + public partial class InputTagHelperTest { + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入事件 + /// + [Fact] + public void TestOnInput() { + _wrapper.SetContextAttribute( UiConst.OnInput, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试回车事件 + /// + [Fact] + public void TestOnEnter() { + _wrapper.SetContextAttribute( UiConst.OnEnter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs index 3491a88d5..0dc016ccf 100644 --- a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs @@ -520,6 +520,22 @@ public void TestAutoLabelFor_5() { result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } + + /// + /// 测试设置输出属性时,仅添加到输入框 + /// + [Fact] + public void TestOutputAttribute() { + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + _wrapper.SetOutputAttribute( "(key)", "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } } } diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.cs index 29ff8fdf9..ecd4ddea8 100644 --- a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.cs @@ -186,17 +186,6 @@ public void TestBindType() { result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } - - /// - /// 测试输入事件 - /// - [Fact] - public void TestOnInput() { - _wrapper.SetContextAttribute( UiConst.OnInput, "a" ); - var result = new StringBuilder(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } } } diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Base.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Base.cs index 455e97009..928eb64c4 100644 --- a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Base.cs +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Base.cs @@ -139,16 +139,5 @@ public void TestBindFormControlName() { result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } - - /// - /// 测试模型变更事件 - /// - [Fact] - public void TestOnModelChange() { - _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); - var result = new StringBuilder(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } } } \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Events.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Events.cs new file mode 100644 index 000000000..afa694e3f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Events.cs @@ -0,0 +1,43 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Textareas { + /// + /// 文本域测试 - 事件测试 + /// + public partial class TextareaTagHelperTest { + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入事件 + /// + [Fact] + public void TestOnInput() { + _wrapper.SetContextAttribute( UiConst.OnInput, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试回车事件 + /// + [Fact] + public void TestOnEnter() { + _wrapper.SetContextAttribute( UiConst.OnEnter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs index faa0b353b..245400538 100644 --- a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs @@ -240,17 +240,6 @@ public void TestAllowClear() { Assert.Equal( result.ToString(), GetResult() ); } - /// - /// 测试输入事件 - /// - [Fact] - public void TestOnInput() { - _wrapper.SetContextAttribute( UiConst.OnInput, "a" ); - var result = new StringBuilder(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - /// /// 测试提及触发元素 ///