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
Step 2: Install Google.Protobuf, Grpc.Net.Client & Grpc.Tools 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
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
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
That’s It. gRPC Client Application is ready !!!
.NET Professional | Microsoft Certified Professional | DZone’s Most Valuable Blogger | Web Developer | Author | Blogger
Doctorate in Computer Science and Engineering
Microsoft Certified Professional (MCP) with over 12+ years of software industry experience including development, implementation & deployment of applications in the .NET framework
Experienced and skilled Agile Developer with a strong record of excellent teamwork, successful coding & project management. Specialises in problem identification and proposal of alternative solutions. Provided knowledge and individual mentoring to team members as needed
Among top 3% overall in terms of contribution on Stack Overflow (~2.3 million people reached my posts). Part of the top 1% Stack Overflow answerers in ASP.NET technology.
DZone’s Most Valuable Blogger (MVB)
Created and actively maintain the TechCartNow.com tech blog while also editing, writing, and researching topics for publication.
Excellent skills in Application Development using C#/Vb.Net, .NET Framework, ASP.NET, MVC, ADO.NET, WCF, WPF, Web API, SQL Server, jQuery, Angular, React, BackboneJS