Creating a gRPC Client on .NET

Learn how to add the gRPC client functionality and its dependencies.

Previously, we created a basic gRPC service that provided some rudimentary chatbot functionality. In this lesson, we'll build a gRPC client capable of connecting to this service. For this purpose, we'll use the most basic .NET application type—a console application.

Adding gRPC client dependencies

We have an interactive code widget below. It has the complete gRPC service application, called BasicGrpcService, that was created previously. In addition, there's a console application called BasicGrpcClient. The setup has been configured to build both applications, launch the BasicGrpcService application in the background, and open the terminal for the BasicGrpcClient application.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.21.5" />
    <PackageReference Include="Grpc.Net.Client" Version="2.48.0" />
    <PackageReference Include="Grpc.Tools" Version="2.48.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>
The full setup containing gRPC client and gRPC server

At this point, we haven't added any gRPC client functionality to the BasicGrpcClient project. However, if we open the BasicGrpcClient.csproj file inside the BasicGrpcClient folder, we can see it has the following NuGet packages pre-installed:

  • Grpc.Net.Client

  • Google.Protobuf

  • Grpc.Tools

These are the NuGet packages that add the minimal required gRPC client functionality. They allow us to add the gRPC client functionality to any .NET application type, including the most basic console application, which is the one we're using here.

Next, we'll need to add the Protobuf file. Since we intend to connect to the server application we created previously, we will need the same Protobuf file. So, we'll just copy the whole Protos folder and all of its content from the BasicGrpcService project folder into the BasicGrpcClient project folder. Then, we'll register this file inside the BasicGrpcClient.csproj file by inserting the following markup:

Press + to interact
<ItemGroup>
<Protobuf Include="Protos\chatbot.proto" GrpcServices="Client" />
</ItemGroup>

This time, the value of the GrpcService attribute is set to Client because we only want to use the client-side functionality in this project.

Now, we're ready to add the actual client code.

Adding gRPC client functionality

If we build our application, the gRPC tools should generate all the required stub objects for us. So, to apply the client functionality, all we need to do is replace the content of the Program.cs file of the BasicGrpcClient project with the following:

Press + to interact
using BasicGrpcService;
using Grpc.Net.Client;
Console.WriteLine("What is your name?");
var name = Console.ReadLine();
Console.WriteLine($"Hello {name}. You can start chatting now.");
Console.WriteLine("Type 'exit' to stop at any time.");
using var channel = GrpcChannel.ForAddress("http://127.0.0.1:5100");
var client = new Chatbot.ChatbotClient(channel);
while (true)
{
var message = Console.ReadLine();
if (message == "exit")
break;
Console.WriteLine($"Message: {message}");
var reply = await client.SendMessageAsync(
new ChatRequest
{
Name = name,
Message = message
});
Console.WriteLine($"Reply: {reply.Message}");
}
Console.ReadKey();

We have now created a console-based chat application. First, the application asks us for our name. Then we can start submitting messages to the server until we type “exit.” For every message we send to the server, the server responds with its own message, which is then logged into the console.

To facilitate this communication, we first instantiate a GrpcChannel object on line 10. Since this object is disposable and it uses some unmanaged resources, we wrap it in a using statement. The address we specify is one of the addresses found in the launchSettings.json file of the BasicGrpcService application.

Once we've created the channel, we create the client implementation from the Chatbot.ChatbotClient class on line 11, which has been auto-generated based on the service definition from the Protobuf file.

Then, we trigger the SendMessage RPC by calling the SendMessageAsync method on the client object on line 22. In the client class, there are two varieties of each RPC: synchronous and asynchronous. The synchronous variant has exactly the same name as the RPC in the service definition inside the Protobuf file. The awaitable asynchronous variant will have the Async suffix added to it, and this is the variant we call.

Once we make the call, the underlying framework will cover everything else. Then, we'll be able to extract the response the same way we obtain return objects from a standard method call.

We're now ready to run our application. However, we might need to apply some additional configuration to our development environment before we do.

Running our application

Let's test our setup. To do so, we need to ensure that both of our applications are running. This will be done automatically when we click the “Run” button in the interactive code widget below. If all of our changes were applied correctly, both applications will be built and launched. Then, we should expect to be greeted with a terminal prompt asking for our name.

syntax = "proto3";

option csharp_namespace = "BasicGrpcService";

package basic_grpc_service;

service Chatbot {
  rpc SendMessage (ChatRequest) returns (ChatReply);
}

message ChatRequest {
  string name = 1;
  string message = 2;
}

message ChatReply {
  string message = 1;
}
Complete application with gRPC client functionality and dependencies

While the applications are running, we can interact with the terminal and keep generating messages for the gRPC server. If we want to stop, we can just type “exit” at any point.