gRPC for .NET: Setting up gRPC Server Application (ASP.NET Core) & Client Application (.NET Core) – Part II: Creating a gRPC Client Application which calls Unary, Server streaming, Client streaming & Bi-directional streaming methods

In this post, I will be explaining how we can quickly setup a gRPC Client Application and perform following gRPC methods calls:

  • Unary call
  • Server streaming call
  • Client streaming call
  • Bi-directional streaming call

Note: For Part I of this post refer gRPC for .NET: Creating a gRPC Server Application & for more info on Protobuf refer Getting started with Protobuf using C# runtime library for Protocol Buffers

In this post, I will be explaining how we can quickly setup a gRPC Client Application.

Getting started with gRPC Client Application

Step 1: Add a new project & pick  Console Application project template in Visual Studio

gRPC Client Project File/Folder Organization

gRPC client application - Project (Files &Folders) Organization

Step 2: Install Google.Protobuf, Grpc.Net.Client & Grpc.Tools Nuget Packages

gRPC client console application Nuget Packages

Step 3: Copy the proto (CommandProcessor.proto) file from gRPC Server Application to Protos root folder

Step 4: Set CommandProcessor.proto file properties: Set Build Action to Protobuf compiler & gRPC Stub Classes to Client Only

gRPC client Set proto file properties Build Action - Protobuf compiler & gRPC Stub Classes - Client Only

Step 4: Build the gRPC Client Application to generate C# code from the CommandProcessor.proto file

Location of generated C# code: \obj\Debug\net5.0\Protos

gRPC Client - Generated C# Classes

Generated C# code contains:

  • A concrete client type (CommandProcessorClient concrete client class) for each service
  • gRPC calls in the .proto (CommandProcessor.proto) file are translated into methods (CommandUnary, CommandUnaryAsync, CommandServerStreaming, CommandClientStreaming, CommandBiDirectionalStreaming) on the concrete (CommandProcessorClient) type, which can be called.
  • Classes for any messages (CommandRequset & CommandRequest class)

Step 5: Setting up  CommandProcessorClient client & calling available gRPC methods

Create a gRPC client instance

using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new CommandProcessor.CommandProcessorClient(channel);

Note: Change the address to the value where your gRPC server application is located. In the below code, I will be using https://localhost:5001

A. Unary call

CommandUnaryAsync call:

using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new CommandProcessor.CommandProcessorClient(channel);
var request = new CommandRequest { Name = "ExecuteTaskA" };
var response = await client.CommandUnaryAsync(request);

B. Server streaming call

CommandServerStreaming call:

using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new CommandProcessor.CommandProcessorClient(channel);
var request = new CommandRequest { Name = "ExecuteTaskB" };
var streamingCall = client.CommandServerStreaming(request);

await foreach (var res in streamingCall.ResponseStream.ReadAllAsync())
{
        // Handle repsponses.
  Console.WriteLine($"{res.Message}");
}

C. Client streaming call

CommandClientStreaming call:

using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new CommandProcessor.CommandProcessorClient(channel);
using AsyncClientStreamingCall<CommandRequest, CommandResponse> response = client.CommandClientStreaming();

var request = new CommandRequest { Name = $"ExecuteTaskX" };
await response.RequestStream.WriteAsync(request);

request = new CommandRequest { Name = $"ExecuteTaskY" };
await response.RequestStream.WriteAsync(request);

request = new CommandRequest { Name = $"ExecuteTaskZ" };
await response.RequestStream.WriteAsync(request);

await response.RequestStream.CompleteAsync();

CommandResponse res = await response;

D. Bi-directional streaming call

CommandBiDirectionalStreaming call:

using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new CommandProcessor.CommandProcessorClient(channel);
using AsyncDuplexStreamingCall<CommandRequest, CommandResponse> response = client.CommandBiDirectionalStreaming();

// Background task to receive messages (response)
var readTask = Task.Run(async () =>
{
  await foreach (CommandResponse res in response.ResponseStream.ReadAllAsync())
  {
    Console.WriteLine(res.Message);
  }
});

// Send messages (request)
Console.WriteLine("Type a command name then press enter.");

while (true)
{
  string commandName = Console.ReadLine();

  if (string.IsNullOrEmpty(commandName))
  {
    break;
  }

  await response.RequestStream.WriteAsync(new CommandRequest { Name = $"{commandName}" });
}

Console.WriteLine("Disconnecting");
await response.RequestStream.CompleteAsync();
await readTask;

Full Sample Code (Program.cs)

using System;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Net.Client;
using SampleApplication.Grpc.Server;

namespace SampleApplication.Grpc.Client
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using var channel = GrpcChannel.ForAddress("https://localhost:5001");
            var client = new CommandProcessor.CommandProcessorClient(channel);

            Console.WriteLine("----------------------------Urary Call Starts-----------------------------------");
            await RemoteCallUrary(client);
            Console.WriteLine("----------------------------Urary Call Ends-----------------------------------");
            Console.WriteLine("-----------------------------Server Streaming (Continuous) Starts----------");
            await RemoteCallServerStreaming(client);
            Console.WriteLine("-----------------------------Server Streaming (Continuous) Ends----------");
            Console.WriteLine("----------------------------Client Streaming Call Starts------------------------");
            await RemoteCallClientStreaming(client);
            Console.WriteLine("----------------------------Client Streaming Call Ends------------------------");
            Console.WriteLine("-----------------------------Bi Directional Streaming Command-------");
            await RemoteCallBiDirectionalStreaming(client);
            Console.WriteLine("-----------------------------Bi Directional Streaming Command-------");

            Console.ReadKey();
        }

        private static async Task RemoteCallUrary(CommandProcessor.CommandProcessorClient client)
        {
            var request = new CommandRequest { Name = "ExecuteTaskA" };
            var response = await client.CommandUnaryAsync(request);
            Console.WriteLine(response.Message);
        }

        private static async Task RemoteCallServerStreaming(CommandProcessor.CommandProcessorClient client)
        {
            var request = new CommandRequest { Name = "ExecuteTaskB" };
            var streamingCall = client.CommandServerStreaming(request);

            await foreach (var res in streamingCall.ResponseStream.ReadAllAsync())
            {
                Console.WriteLine($"{res.Message}");
            }
        }

        private static async Task RemoteCallServerStreamingContinuous(CommandProcessor.CommandProcessorClient client)
        {
            var request = new CommandRequest { Name = "ExecuteTaskC" };
            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(100));
            var streamingCall = client.CommandServerStreaming(request, cancellationToken: cts.Token);

            try
            {
                await foreach (var res in streamingCall.ResponseStream.ReadAllAsync(cancellationToken: cts.Token))
                {
                    Console.WriteLine($"{res.Message}");
                }
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled)
            {
                Console.WriteLine("Stream cancelled.");
            }
        }

        private static async Task RemoteCallClientStreaming(CommandProcessor.CommandProcessorClient client)
        {
            using AsyncClientStreamingCall<CommandRequest, CommandResponse> response = client.CommandClientStreaming();

            var request = new CommandRequest { Name = $"ExecuteTaskX" };
            await response.RequestStream.WriteAsync(request);

            request = new CommandRequest { Name = $"ExecuteTaskY" };
            await response.RequestStream.WriteAsync(request);

            request = new CommandRequest { Name = $"ExecuteTaskZ" };
            await response.RequestStream.WriteAsync(request);

            await response.RequestStream.CompleteAsync();

            CommandResponse res = await response;

            Console.WriteLine(res.Message);
        }

        private static async Task RemoteCallBiDirectionalStreaming(CommandProcessor.CommandProcessorClient client)
        {
            using AsyncDuplexStreamingCall<CommandRequest, CommandResponse> response = client.CommandBiDirectionalStreaming();

            // Starting background task to receive messages.
            var readTask = Task.Run(async () =>
            {
                await foreach (CommandResponse res in response.ResponseStream.ReadAllAsync())
                {
                    Console.WriteLine(res.Message);
                }
            });

            // Starting to send messages
            Console.WriteLine("Type a command name then press enter.");

            while (true)
            {
                string commandName = Console.ReadLine();

                if (string.IsNullOrEmpty(commandName))
                {
                    break;
                }

                await response.RequestStream.WriteAsync(new CommandRequest { Name = $"{commandName}" });
            }

            Console.WriteLine("Disconnecting");
            await response.RequestStream.CompleteAsync();
            await readTask;
        }
    }
}

Step 6: Start your gRPC Server Application & then start your gRPC Client Application

Set multiple startup projects in Visual Studio &  hit Ctrl + F5

gRPC - VS solution set multiple startup projects

That’s It. gRPC Client Application is ready !!!

gRPC client

 

 

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *