public static async Task <ChannelReader <TResult> > StreamAsChannelCoreAsync <TResult>(this HubConnection hubConnection, string methodName, object[] args, CancellationToken cancellationToken = default)
        {
            if (hubConnection == null)
            {
                throw new ArgumentNullException(nameof(hubConnection));
            }

            var inputChannel = await hubConnection.StreamAsChannelCoreAsync(methodName, typeof(TResult), args, cancellationToken);

            var outputChannel = Channel.CreateUnbounded <TResult>();

            // Local function to provide a way to run async code as fire-and-forget
            // The output channel is how we signal completion to the caller.
            async Task RunChannel()
            {
                try
                {
                    while (await inputChannel.WaitToReadAsync())
                    {
                        while (inputChannel.TryRead(out var item))
                        {
                            while (!outputChannel.Writer.TryWrite((TResult)item))
                            {
                                if (!await outputChannel.Writer.WaitToWriteAsync())
                                {
                                    // Failed to write to the output channel because it was closed. Nothing really we can do but abort here.
                                    return;
                                }
                            }
                        }
                    }

                    // Manifest any errors in the completion task
                    await inputChannel.Completion;
                }
                catch (Exception ex)
                {
                    outputChannel.Writer.TryComplete(ex);
                }
                finally
                {
                    // This will safely no-op if the catch block above ran.
                    outputChannel.Writer.TryComplete();
                }
            }

            _ = RunChannel();

            return(outputChannel.Reader);
        }
Example #2
0
        /// <summary>
        /// Invokes a streaming hub method on the server using the specified method name, return type and arguments.
        /// </summary>
        /// <typeparam name="TResult">The return type of the streaming server method.</typeparam>
        /// <param name="hubConnection">The hub connection.</param>
        /// <param name="methodName">The name of the server method to invoke.</param>
        /// <param name="args">The arguments used to invoke the server method.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None" />.</param>
        /// <returns>
        /// A <see cref="Task{TResult}"/> that represents the asynchronous invoke.
        /// The <see cref="Task{TResult}.Result"/> property returns a <see cref="ChannelReader{T}"/> for the streamed hub method values.
        /// </returns>
        public static async Task <ChannelReader <TResult> > StreamAsChannelCoreAsync <TResult>(this HubConnection hubConnection, string methodName, object?[] args, CancellationToken cancellationToken = default)
        {
            if (hubConnection == null)
            {
                throw new ArgumentNullException(nameof(hubConnection));
            }

            var inputChannel = await hubConnection.StreamAsChannelCoreAsync(methodName, typeof(TResult), args, cancellationToken);

            var outputChannel = Channel.CreateUnbounded <TResult>();

            // Intentionally avoid passing the CancellationToken to RunChannel. The token is only meant to cancel the intial setup, not the enumeration.
            _ = RunChannel(inputChannel, outputChannel);

            return(outputChannel.Reader);
        }
 public static Task <ChannelReader <TResult> > StreamAsChannelAsync <TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, CancellationToken cancellationToken = default)
 {
     return(hubConnection.StreamAsChannelCoreAsync <TResult>(methodName, new[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 }, cancellationToken));
 }
 public static Task <ChannelReader <TResult> > StreamAsChannelAsync <TResult>(this HubConnection hubConnection, string methodName, object arg1, CancellationToken cancellationToken = default)
 {
     return(hubConnection.StreamAsChannelCoreAsync <TResult>(methodName, new[] { arg1 }, cancellationToken));
 }
 public static Task <ChannelReader <TResult> > StreamAsChannelAsync <TResult>(this HubConnection hubConnection, string methodName, CancellationToken cancellationToken = default)
 {
     return(hubConnection.StreamAsChannelCoreAsync <TResult>(methodName, Array.Empty <object>(), cancellationToken));
 }