@@ -29,6 +29,7 @@ public static partial class RequestDelegateFactory
29
29
private static readonly MethodInfo ExecuteValueTaskOfStringMethod = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteValueTaskOfString ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
30
30
private static readonly MethodInfo ExecuteTaskResultOfTMethod = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteTaskResult ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
31
31
private static readonly MethodInfo ExecuteValueResultTaskOfTMethod = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteValueTaskResult ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
32
+ private static readonly MethodInfo ExecuteObjectReturnMethod = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteObjectReturn ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
32
33
private static readonly MethodInfo GetRequiredServiceMethod = typeof ( ServiceProviderServiceExtensions ) . GetMethod ( nameof ( ServiceProviderServiceExtensions . GetRequiredService ) , BindingFlags . Public | BindingFlags . Static , new Type [ ] { typeof ( IServiceProvider ) } ) ! ;
33
34
private static readonly MethodInfo ResultWriteResponseAsyncMethod = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteResultWriteResponse ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
34
35
private static readonly MethodInfo StringResultWriteResponseAsyncMethod = GetMethodInfo < Func < HttpResponse , string , Task > > ( ( response , text ) => HttpResponseWritingExtensions . WriteAsync ( response , text , default ) ) ;
@@ -338,6 +339,21 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
338
339
{
339
340
return Expression . Block ( methodCall , CompletedTaskExpr ) ;
340
341
}
342
+ else if ( returnType == typeof ( object ) )
343
+ {
344
+ return Expression . Call ( ExecuteObjectReturnMethod , methodCall , HttpContextExpr ) ;
345
+ }
346
+ else if ( returnType == typeof ( ValueTask < object > ) )
347
+ {
348
+ // REVIEW: We can avoid this box if it becomes a performance issue
349
+ var box = Expression . TypeAs ( methodCall , typeof ( object ) ) ;
350
+ return Expression . Call ( ExecuteObjectReturnMethod , box , HttpContextExpr ) ;
351
+ }
352
+ else if ( returnType == typeof ( Task < object > ) )
353
+ {
354
+ var convert = Expression . Convert ( methodCall , typeof ( object ) ) ;
355
+ return Expression . Call ( ExecuteObjectReturnMethod , convert , HttpContextExpr ) ;
356
+ }
341
357
else if ( AwaitableInfo . IsTypeAwaitable ( returnType , out _ ) )
342
358
{
343
359
if ( returnType == typeof ( Task ) )
@@ -632,6 +648,61 @@ private static MemberInfo GetMemberInfo<T>(Expression<T> expr)
632
648
return mc . Member ;
633
649
}
634
650
651
+ // The result of the method is null so we fallback to some runtime logic.
652
+ // First we check if the result is IResult, Task<IResult> or ValueTask<IResult>. If
653
+ // it is, we await if necessary then execute the result.
654
+ // Then we check to see if it's Task<object> or ValueTask<object>. If it is, we await
655
+ // if necessary and restart the cycle until we've reached a terminal state (unknown type).
656
+ // We currently don't handle Task<unknown> or ValueTask<unknown>. We can support this later if this
657
+ // ends up being a common scenario.
658
+ private static async Task ExecuteObjectReturn ( object ? obj , HttpContext httpContext )
659
+ {
660
+ // See if we need to unwrap Task<object> or ValueTask<object>
661
+ if ( obj is Task < object > taskObj )
662
+ {
663
+ obj = await taskObj ;
664
+ }
665
+ else if ( obj is ValueTask < object > valueTaskObj )
666
+ {
667
+ obj = await valueTaskObj ;
668
+ }
669
+ else if ( obj is Task < IResult ? > task )
670
+ {
671
+ await ExecuteTaskResult ( task , httpContext ) ;
672
+ return ;
673
+ }
674
+ else if ( obj is ValueTask < IResult ? > valueTask )
675
+ {
676
+ await ExecuteValueTaskResult ( valueTask , httpContext ) ;
677
+ return ;
678
+ }
679
+ else if ( obj is Task < string ? > taskString )
680
+ {
681
+ await ExecuteTaskOfString ( taskString , httpContext ) ;
682
+ return ;
683
+ }
684
+ else if ( obj is ValueTask < string ? > valueTaskString )
685
+ {
686
+ await ExecuteValueTaskOfString ( valueTaskString , httpContext ) ;
687
+ return ;
688
+ }
689
+
690
+ // Terminal built ins
691
+ if ( obj is IResult result )
692
+ {
693
+ await ExecuteResultWriteResponse ( result , httpContext ) ;
694
+ }
695
+ else if ( obj is string stringValue )
696
+ {
697
+ await httpContext . Response . WriteAsync ( stringValue ) ;
698
+ }
699
+ else
700
+ {
701
+ // Otherwise, we JSON serialize when we reach the terminal state
702
+ await httpContext . Response . WriteAsJsonAsync ( obj ) ;
703
+ }
704
+ }
705
+
635
706
private static Task ExecuteTask < T > ( Task < T > task , HttpContext httpContext )
636
707
{
637
708
EnsureRequestTaskNotNull ( task ) ;
@@ -715,12 +786,12 @@ private static Task ExecuteValueTaskResult<T>(ValueTask<T?> task, HttpContext ht
715
786
{
716
787
static async Task ExecuteAwaited ( ValueTask < T > task , HttpContext httpContext )
717
788
{
718
- await EnsureRequestResultNotNull ( await task ) ! . ExecuteAsync ( httpContext ) ;
789
+ await EnsureRequestResultNotNull ( await task ) . ExecuteAsync ( httpContext ) ;
719
790
}
720
791
721
792
if ( task . IsCompletedSuccessfully )
722
793
{
723
- return EnsureRequestResultNotNull ( task . GetAwaiter ( ) . GetResult ( ) ) ! . ExecuteAsync ( httpContext ) ;
794
+ return EnsureRequestResultNotNull ( task . GetAwaiter ( ) . GetResult ( ) ) . ExecuteAsync ( httpContext ) ;
724
795
}
725
796
726
797
return ExecuteAwaited ( task ! , httpContext ) ;
@@ -730,12 +801,12 @@ private static async Task ExecuteTaskResult<T>(Task<T?> task, HttpContext httpCo
730
801
{
731
802
EnsureRequestTaskOfNotNull ( task ) ;
732
803
733
- await EnsureRequestResultNotNull ( await task ) ! . ExecuteAsync ( httpContext ) ;
804
+ await EnsureRequestResultNotNull ( await task ) . ExecuteAsync ( httpContext ) ;
734
805
}
735
806
736
- private static async Task ExecuteResultWriteResponse ( IResult result , HttpContext httpContext )
807
+ private static async Task ExecuteResultWriteResponse ( IResult ? result , HttpContext httpContext )
737
808
{
738
- await EnsureRequestResultNotNull ( result ) ! . ExecuteAsync ( httpContext ) ;
809
+ await EnsureRequestResultNotNull ( result ) . ExecuteAsync ( httpContext ) ;
739
810
}
740
811
741
812
private class FactoryContext
0 commit comments