Building a gRPC Client in .NET
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.
- Introduction to gRPC
- Building a gRPC server with Go
- Building a gRPC server with .NET
- Building a gRPC client with Go
- Building a gRPC client with .NET (You are here)
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.
- Scaffold a .NET console project.
- Implementing the gRPC client.
- Communicating with the server.
In a nutshell, we will be generating the client for the server we built in our previous post.
💡 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.
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.
- 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 aGrpcChannelOptions
object as the second parameter to define client options. Here’s a list for that. - 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 samechannel
object for all of those. - 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. - 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
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.
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 👋