Sahan Serasinghe

Software Engineer | Data Science Enthusiast

Building a gRPC Client in .NET

2022-03-18architecture 5 min read

Introduction

In this article, we will take a look at how to create a simple gRPC client with .NET and communicate with a server. This is the final post of the blog series where we talk about building gRPC services.

Motivation

This is the fifth part of an articles series on gRPC. If you didn’t catch the previous ones please feel free to do so. The links are down below.

Please note that this is intended for anyone who’s interested in getting started with gRPC. If you’re not, please feel free to skip this article.

Plan

The plan for this article is as follows.

  1. Scaffold a .NET console project.
  2. Implementing the gRPC client.
  3. Communicating with the server.

In a nutshell, we will be generating the client for the server we built in our previous post.

building-grpc-client-dotnet-1.png

💡  As always, all the code samples documentation can be found at: https://github.com/sahansera/dotnet-grpc

Prerequisites

  • .NET 6 SDK
  • Visual Studio Code or IDE of your choice
  • gRPC compiler

Please note that I’m using some of the commands that are macOS specific. Please follow this link to set it up if you are on a different OS.

To install Protobuf compiler:

brew install protobuf

Project Structure

We can use .NET’s tooling to generate a sample gRPC project. Run the following command at the root of your workspace. Remember how we used dotnet new grpc command to scaffold the server project? For this one though, it can simply be a console app.

dotnet new console -o BookshopClient

Your project structure should look like this.

building-grpc-client-dotnet-2.png

You must be wondering if this is a console app how does it know how to generate the client stubs? Well, it doesn’t. You have to add the following packages to the project first.

dotnet add BookshopClient.csproj package Grpc.Net.Client
dotnet add BookshopClient.csproj package Google.Protobuf
dotnet add BookshopClient.csproj package Grpc.Tools

Once everything’s installed, we can proceed with the rest of the steps.

Generating the client stubs

We will be using the same Protobuf files that we generated in our previous step. If you haven’t seen that already head over to my previous post.

Open up the BookshopClient.csproj file you need to add the following lines:

...
<ItemGroup>
  <Protobuf Include="../proto/bookshop.proto" GrpcServices="Client" />
</ItemGroup>
...

As you can see we will be reusing our Bookshop.proto file. in this example too. One thing to note here is that we have updated the GrpcServices attribute to be Client.

Implementing the gRPC client

Let’s update the Program.cs file to connect to and get the response from the server.

using System.Threading.Tasks;
using Grpc.Net.Client;
using Bookshop;

// The port number must match the port of the gRPC server.
using var channel = GrpcChannel.ForAddress("http://localhost:5000");
var client = new Inventory.InventoryClient(channel);
var reply = await client.GetBookListAsync(new GetBookListRequest { });

Console.WriteLine("Greeting: " + reply.Books);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();

This is based on the example given on the Microsoft docs site btw. What I really like about the above code is how easy it is to read. So here’s what happens.

building-grpc-client-dotnet-3.png

  1. We first create a gRPC channel with GrpcChannel.ForAddress to the server by giving its URI and port. A client can reuse the same channel object to communicate with a gRPC server. This is an expensive operation compared to invoking a gRPC method on the server. You can also pass in a GrpcChannelOptions object as the second parameter to define client options. Here’s a list for that.
  2. Then we use the auto-generated method Inventory.InventoryClient by leveraging the channel we created above. One thing to note here is that, if your server has multiple services, you can still use the same channel object for all of those.
  3. We call the GetBookListAsync on our server. By the way, this is a Unary call, we will go through other client-server communication mechanisms in a separate post.
  4. Our GetBookList method gets called on the server and returns the list of books.

Now that we know how the requests work, let’s see this in action.

Communicating with the server

Let’s spin up the server that we built in my previous post first. This will be up and running at port 5000.

dotnet run --project BookshopServer/BookshopServer.csproj

building-grpc-client-dotnet-4.png

For the client-side, we invoke a similar command.

dotnet run --project BookshopClient/BookshopClient.csproj

And in the terminal, we will get the following outputs.

building-grpc-client-dotnet-5.png

Nice! as you can see it’s not that hard to get everything working 🎉 One thing to note is that we left out the details about TLS and different ways to communicate with the server (i.e. Unary, streaming etc.). I will cover such topics in-depth in the future.

Conclusion

In this article, we looked at how to reuse our Protobuf files to create a client to interact with the server we created in the previous post.

I hope this article series cleared up a lot of confusion that you had about gRPC. Please feel free to share your questions, thoughts, or feedback in the comments section below. Until next time 👋

References

Loading...
Sahan Serasinghe - Personal Blog

Sahan Serasinghe Software Engineer at GitHub | Azure Solutions Architect | Master of Data Science at UIUC | CKAD