diff --git a/src/Cnblogs.Architecture.Ddd.Cqrs.AspNetCore/CqrsRouteMapper.cs b/src/Cnblogs.Architecture.Ddd.Cqrs.AspNetCore/CqrsRouteMapper.cs index 5b4ad3a..ab233a3 100644 --- a/src/Cnblogs.Architecture.Ddd.Cqrs.AspNetCore/CqrsRouteMapper.cs +++ b/src/Cnblogs.Architecture.Ddd.Cqrs.AspNetCore/CqrsRouteMapper.cs @@ -21,11 +21,29 @@ public static class CqrsRouteMapper private static readonly string[] GetAndHeadMethods = { "GET", "HEAD" }; - private static readonly List PostCommandPrefixes = new() { "Create", "Add", "New" }; + private static readonly List PostCommandPrefixes = new() + { + "Create", + "Add", + "New" + }; - private static readonly List PutCommandPrefixes = new() { "Update", "Modify", "Replace", "Alter" }; + private static readonly List PutCommandPrefixes = new() + { + "Update", + "Modify", + "Replace", + "Alter" + }; - private static readonly List DeleteCommandPrefixes = new() { "Delete", "Remove", "Clean", "Clear", "Purge" }; + private static readonly List DeleteCommandPrefixes = new() + { + "Delete", + "Remove", + "Clean", + "Clear", + "Purge" + }; /// /// Map a query API, using GET method. would been constructed from route and query string. @@ -96,14 +114,7 @@ public static IEndpointConventionBuilder MapQuery( string nullRouteParameterPattern = "-", bool enableHead = false) { - var isQuery = handler.Method.ReturnType.GetInterfaces().Where(x => x.IsGenericType) - .Any(x => QueryTypes.Contains(x.GetGenericTypeDefinition())); - if (isQuery == false) - { - throw new ArgumentException( - "delegate does not return a query, please make sure it returns object that implement IQuery<> or IListQuery<> or interface that inherit from them"); - } - + var returnType = EnsureReturnTypeIsQuery(handler); if (mapNullableRouteParameters is MapNullableRouteParameter.Disable) { return MapRoutes(route); @@ -118,7 +129,7 @@ public static IEndpointConventionBuilder MapQuery( var parsedRoute = RoutePatternFactory.Parse(route); var context = new NullabilityInfoContext(); - var nullableRouteProperties = handler.Method.ReturnType.GetProperties() + var nullableRouteProperties = returnType.GetProperties() .Where( p => p.GetMethod != null && p.SetMethod != null @@ -209,8 +220,7 @@ public static IEndpointConventionBuilder MapCommand( [StringSyntax("Route")] string route, Delegate handler) { - EnsureDelegateReturnTypeIsCommand(handler); - var commandTypeName = handler.Method.ReturnType.Name; + var commandTypeName = EnsureReturnTypeIsCommand(handler).Name; if (PostCommandPrefixes.Any(x => commandTypeName.StartsWith(x))) { return app.MapPostCommand(route, handler); @@ -255,7 +265,7 @@ public static IEndpointConventionBuilder MapPostCommand( [StringSyntax("Route")] string route, Delegate handler) { - EnsureDelegateReturnTypeIsCommand(handler); + EnsureReturnTypeIsCommand(handler); return app.MapPost(route, handler).AddEndpointFilter(); } @@ -285,7 +295,7 @@ public static IEndpointConventionBuilder MapPutCommand( [StringSyntax("Route")] string route, Delegate handler) { - EnsureDelegateReturnTypeIsCommand(handler); + EnsureReturnTypeIsCommand(handler); return app.MapPut(route, handler).AddEndpointFilter(); } @@ -315,7 +325,7 @@ public static IEndpointConventionBuilder MapDeleteCommand( [StringSyntax("Route")] string route, Delegate handler) { - EnsureDelegateReturnTypeIsCommand(handler); + EnsureReturnTypeIsCommand(handler); return app.MapDelete(route, handler).AddEndpointFilter(); } @@ -385,15 +395,42 @@ public static IEndpointRouteBuilder StopMappingPrefixToDelete(this IEndpointRout return app; } - private static void EnsureDelegateReturnTypeIsCommand(Delegate handler) + private static Type EnsureReturnTypeIsCommand(Delegate handler) { - var isCommand = handler.Method.ReturnType.GetInterfaces().Where(x => x.IsGenericType) + var returnType = handler.Method.ReturnType; + if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + returnType = returnType.GenericTypeArguments.First(); + } + + var isCommand = returnType.GetInterfaces().Where(x => x.IsGenericType) .Any(x => CommandTypes.Contains(x.GetGenericTypeDefinition())); if (isCommand == false) { throw new ArgumentException( "handler does not return command, check if delegate returns type that implements ICommand<> or ICommand<,>"); } + + return returnType; + } + + private static Type EnsureReturnTypeIsQuery(Delegate handler) + { + var returnType = handler.Method.ReturnType; + if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + returnType = returnType.GenericTypeArguments.First(); + } + + var isCommand = returnType.GetInterfaces().Where(x => x.IsGenericType) + .Any(x => QueryTypes.Contains(x.GetGenericTypeDefinition())); + if (isCommand == false) + { + throw new ArgumentException( + "handler does not return query, check if delegate returns type that implements IQuery<>"); + } + + return returnType; } private static List GetNotEmptySubsets(ICollection items) diff --git a/src/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent.csproj b/src/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent.csproj index c8a0773..53f1d17 100644 --- a/src/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent.csproj +++ b/src/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Cnblogs.Architecture.Ddd.Infrastructure.Dapper.Clickhouse/Cnblogs.Architecture.Ddd.Infrastructure.Dapper.Clickhouse.csproj b/src/Cnblogs.Architecture.Ddd.Infrastructure.Dapper.Clickhouse/Cnblogs.Architecture.Ddd.Infrastructure.Dapper.Clickhouse.csproj index 6cf13b9..c4357b8 100644 --- a/src/Cnblogs.Architecture.Ddd.Infrastructure.Dapper.Clickhouse/Cnblogs.Architecture.Ddd.Infrastructure.Dapper.Clickhouse.csproj +++ b/src/Cnblogs.Architecture.Ddd.Infrastructure.Dapper.Clickhouse/Cnblogs.Architecture.Ddd.Infrastructure.Dapper.Clickhouse.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/Cnblogs.Architecture.Ddd.Infrastructure.EntityFramework/Cnblogs.Architecture.Ddd.Infrastructure.EntityFramework.csproj b/src/Cnblogs.Architecture.Ddd.Infrastructure.EntityFramework/Cnblogs.Architecture.Ddd.Infrastructure.EntityFramework.csproj index f5f841d..47f95bf 100644 --- a/src/Cnblogs.Architecture.Ddd.Infrastructure.EntityFramework/Cnblogs.Architecture.Ddd.Infrastructure.EntityFramework.csproj +++ b/src/Cnblogs.Architecture.Ddd.Infrastructure.EntityFramework/Cnblogs.Architecture.Ddd.Infrastructure.EntityFramework.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Cnblogs.Architecture.IntegrationTestProject/Cnblogs.Architecture.IntegrationTestProject.csproj b/test/Cnblogs.Architecture.IntegrationTestProject/Cnblogs.Architecture.IntegrationTestProject.csproj index 1b9b076..799840f 100644 --- a/test/Cnblogs.Architecture.IntegrationTestProject/Cnblogs.Architecture.IntegrationTestProject.csproj +++ b/test/Cnblogs.Architecture.IntegrationTestProject/Cnblogs.Architecture.IntegrationTestProject.csproj @@ -1,7 +1,7 @@  - + diff --git a/test/Cnblogs.Architecture.IntegrationTestProject/Program.cs b/test/Cnblogs.Architecture.IntegrationTestProject/Program.cs index 3d5f4ed..6c9aa95 100644 --- a/test/Cnblogs.Architecture.IntegrationTestProject/Program.cs +++ b/test/Cnblogs.Architecture.IntegrationTestProject/Program.cs @@ -7,6 +7,7 @@ using Cnblogs.Architecture.IntegrationTestProject.Application.Queries; using Cnblogs.Architecture.IntegrationTestProject.Payloads; using Cnblogs.Architecture.TestIntegrationEvents; +using Microsoft.AspNetCore.Mvc; var builder = WebApplication.CreateBuilder(args); @@ -36,10 +37,18 @@ var apis = app.NewVersionedApi(); var v1 = apis.MapGroup("/api/v{version:apiVersion}").HasApiVersion(1); -v1.MapQuery("apps/{appId}/strings/{stringId:int}/value", MapNullableRouteParameter.Enable, enableHead: true); -v1.MapQuery("strings/{id:int}"); +v1.MapQuery( + "apps/{appId}/strings/{stringId:int}/value", + MapNullableRouteParameter.Enable, + enableHead: true); +v1.MapQuery( + "strings/{stringId:int}", + async (int stringId, [FromQuery] bool found = true) + => await Task.FromResult(new GetStringQuery(StringId: stringId, Found: found))); v1.MapQuery("strings"); -v1.MapCommand("strings", (CreatePayload payload) => new CreateCommand(payload.NeedError, payload.Data)); +v1.MapCommand( + "strings", + (CreatePayload payload) => Task.FromResult(new CreateCommand(payload.NeedError, payload.Data))); v1.MapCommand( "strings/{id:int}", (int id, UpdatePayload payload) => new UpdateCommand(id, payload.NeedValidationError, payload.NeedExecutionError)); diff --git a/test/Cnblogs.Architecture.IntegrationTests/Cnblogs.Architecture.IntegrationTests.csproj b/test/Cnblogs.Architecture.IntegrationTests/Cnblogs.Architecture.IntegrationTests.csproj index 11f45a0..2b6ad3f 100644 --- a/test/Cnblogs.Architecture.IntegrationTests/Cnblogs.Architecture.IntegrationTests.csproj +++ b/test/Cnblogs.Architecture.IntegrationTests/Cnblogs.Architecture.IntegrationTests.csproj @@ -1,7 +1,7 @@ - + diff --git a/test/Cnblogs.Architecture.UnitTests/Cnblogs.Architecture.UnitTests.csproj b/test/Cnblogs.Architecture.UnitTests/Cnblogs.Architecture.UnitTests.csproj index 6545a31..6db550f 100644 --- a/test/Cnblogs.Architecture.UnitTests/Cnblogs.Architecture.UnitTests.csproj +++ b/test/Cnblogs.Architecture.UnitTests/Cnblogs.Architecture.UnitTests.csproj @@ -1,7 +1,7 @@ - +