@@ -173,4 +173,47 @@ async Task AssertClientResult(Task<int> resultTask)
173173 await AssertClientResult ( context . Clients . Client ( connectionId ) . GetClientResult ( 1 ) ) ;
174174 }
175175 }
176+
177+ [ Fact ]
178+ public async Task CanReturnClientResultToTypedHubThreeWays ( )
179+ {
180+ using ( StartVerifiableLog ( ) )
181+ {
182+ var serviceProvider = HubConnectionHandlerTestUtils . CreateServiceProvider ( builder =>
183+ {
184+ // Waiting for a client result blocks the hub dispatcher pipeline, need to allow multiple invocations
185+ builder . AddSignalR ( o => o . MaximumParallelInvocationsPerClient = 2 ) ;
186+ } , LoggerFactory ) ;
187+ var connectionHandler = serviceProvider . GetService < HubConnectionHandler < HubT > > ( ) ;
188+
189+ using var client = new TestClient ( invocationBinder : new GetClientResultThreeWaysInvocationBinder ( ) ) ;
190+
191+ var connectionHandlerTask = await client . ConnectAsync ( connectionHandler ) . DefaultTimeout ( ) ;
192+
193+ var invocationId = await client . SendHubMessageAsync ( new InvocationMessage (
194+ invocationId : "1" ,
195+ nameof ( HubT . GetClientResultThreeWays ) ,
196+ new object [ ] { 5 , 6 , 7 } ) ) . DefaultTimeout ( ) ;
197+
198+ // Send back "value + 4" to all three invocations.
199+ for ( int i = 0 ; i < 3 ; i ++ )
200+ {
201+ // Hub asks client for a result, this is an invocation message with an ID.
202+ var invocationMessage = Assert . IsType < InvocationMessage > ( await client . ReadAsync ( ) . DefaultTimeout ( ) ) ;
203+ Assert . NotNull ( invocationMessage . InvocationId ) ;
204+ var res = 4 + ( int ) invocationMessage . Arguments [ 0 ] ;
205+ await client . SendHubMessageAsync ( CompletionMessage . WithResult ( invocationMessage . InvocationId , res ) ) . DefaultTimeout ( ) ;
206+ }
207+
208+ var completion = Assert . IsType < CompletionMessage > ( await client . ReadAsync ( ) . DefaultTimeout ( ) ) ;
209+ Assert . Equal ( new ClientResults ( 9 , 10 , 11 ) , completion . Result ) ;
210+ }
211+ }
212+
213+ private class GetClientResultThreeWaysInvocationBinder : IInvocationBinder
214+ {
215+ public IReadOnlyList < Type > GetParameterTypes ( string methodName ) => new [ ] { typeof ( int ) } ;
216+ public Type GetReturnType ( string invocationId ) => typeof ( ClientResults ) ;
217+ public Type GetStreamItemType ( string streamId ) => throw new NotImplementedException ( ) ;
218+ }
176219}
0 commit comments