Building a gRPC Server in .NET
Tags: dotnet, grpc, tutorial, webdev
Published: 2022-03-05 21:58:38

---
title: Building a gRPC Server in .NET
published: true
date: 2022-03-05 21:58:00 UTC
tags: dotnet, grpc, tutorial, webdev
canonical_url: https://sahansera.dev/building-grpc-server-dotnet/
cover_image: https://sahansera.dev/static/a915143d960bcf637248a5bcd2e52e2f/f1b1b/building-grpc-server-dotnet.jpg
---

## Introduction

In this article, we will look at how to build a simple web service with gRPC in .NET. We will keep our changes to minimal and leverage the same Protocol Buffer IDL we used in my previous [post](https://sahansera.dev/building-grpc-server-go/). We will also go through some common problems that you might face when building a gRPC server in .NET.

## Motivation

For this article also we will be using the Online Bookshop example and leveraging the same Protobufs as we saw before. For those who aren’t familiar with or missed this series, you can find them from [here](https://sahansera.dev/introduction-to-grpc/).

- [Introduction to gRPC](https://sahansera.dev/introduction-to-grpc/)
- [Building a gRPC server with Go](https://sahansera.dev/building-grpc-server-go/)
- [Building a gRPC client with .NET](https://sahansera.dev/building-grpc-server-dotnet/)  (You are here)
- [Building a gRPC client with Go](https://sahansera.dev/building-grpc-client-go)
- [Building a gRPC client with .NET](https://sahansera.dev/building-grpc-client-dotnet)

We will be covering steps 1 and 2 in the following diagram.

![building-grpc-server-dotnet-1.png](https://sahansera.dev/static/c1481df614d654fbbcaede50e8cf20db/5a190/building-grpc-server-dotnet-1.png "building-grpc-server-dotnet-1.png")

## Plan

So this is what we are trying to achieve.

1. Generate the `.proto` IDL stubs.
2. Write the business logic for our service methods.
3. Spin up a gRPC server on a given port.

In a nutshell, we will be covering the following items on our initial diagram.

> 💡  As always, all the code samples documentation can be found at: [https://github.com/sahansera/dotnet-grpc](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](https://grpc.io/docs/languages/go/quickstart/)** to set it up if you are on a different OS.

To install Protobuf compiler:

```sh
brew install protobuf
```

## Project Structure

We can use the the .NET’s tooling to generate a sample gRPC project. Run the following command in at the root of your workspace.

```sh
dotnet new grpc -o BookshopServer
```

Once you run the above command, you will see the following structure.

![building-grpc-server-dotnet-2.png](https://sahansera.dev/static/fcbb13c0b3a020db98468fbf0c1e6f9d/87254/building-grpc-server-dotnet-2.png "building-grpc-server-dotnet-2.png")

We also need to configure the SSL trust:

```sh
dotnet dev-certs https --trust
```

As you might have guessed, this is like a default template and it already has a lot of things wired up for us like the `Protos` folder.

## Generating the server stubs

Usually, we would have to invoke the protocol buffer compiler to generate the code for the target language (as we saw in my previous article). However, for .NET they have streamlined the code generation process. They use the [Grpc.Tools](https://www.nuget.org/packages/Grpc.Tools/) NuGet package with MSBuild to provide automatic code generation, which is pretty neat! 👏

If you open up the Bookshop.csproj file you will find the following lines:

```xml
...
<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>
...
```

We are going to replace greet.proto with our [Bookshop.proto](https://github.com/sahansera/dotnet-grpc/blob/main/Proto/bookshop.proto) file.

![building-grpc-server-dotnet-3](https://sahansera.dev/static/378d6ab2ca89a5fafc8a261295613f8c/5a190/building-grpc-server-dotnet-3.png "building-grpc-server-dotnet-3")

We will also update our [csproj](https://github.com/sahansera/dotnet-grpc/blob/main/BookshopServer/BookshopServer.csproj) file like so:

```xml
<ItemGroup>
  <Protobuf Include="../proto/bookshop.proto" GrpcServices="Server" />
</ItemGroup>
```

## Implementing the Server

The implementation part is easy! Let’s clean up the GreeterService that comes default and add a new file called `InventoryService.cs`

```sh
rm BookshopServer/Services/GreeterService.cs
code BookshopServer/Services/InventoryService.cs
```

This is what our service is going to look like.

[InventoryService.cs](https://github.com/sahansera/dotnet-grpc/blob/main/BookshopServer/Services/InventoryService.cs)

![building-grpc-server-dotnet-4](https://sahansera.dev/static/c2466f61384b1486a03db79f46080db6/5a190/building-grpc-server-dotnet-4.png "building-grpc-server-dotnet-4")

Let’s go through the code step by step.

1. `Inventory.InventoryBase` is an abstract class that got auto-generated (in your `obj/debug` folder) from our protobuf file.
2. `GetBookList` method’s stub is already generated for us in the `InventoryBase` class and that’s why we are overriding it. Again, this is the RPC call we defined in our protobuf definition. This method takes in a `GetBookListRequest` which defines what the request looks like and a `ServerCallContext` param which contains the headers, auth context etc.
3. Rest of the code is pretty easy - we prepare the response and return it back to the caller/client. It’s worth noting that we never defined the `GetBookListRequest` `GetBookListResponse` types ourselves, manually. The gRPC tooling for .NET has already created these for us under the `Bookshop` namespace.

Make sure to update the [Program.cs](https://github.com/sahansera/dotnet-grpc/blob/main/BookshopServer/Program.cs) to reflect the new service as well.

```csharp
// ...
app.MapGrpcService<InventoryService>();
// ...
```

And then we can run the server with the following command.

```sh
dotnet run --project BookshopServer/BookshopServer.csproj
```

![building-grpc-server-dotnet-5.png](https://sahansera.dev/static/2d078f119b64e3731370c7f2afce9b0b/5a190/building-grpc-server-dotnet-5.png "building-grpc-server-dotnet-5.png")

We are almost there! Remember we can’t access the service yet through the browser since browsers don’t understand binary protocols. In the next step, we will to test our service 🎉

## Common Errors

A common [error](https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-6.0#unable-to-start-aspnet-core-grpc-app-on-macos) you’d find on macOS systems with .NET is HTTP/2 and TLS issue shown below.

![building-grpc-server-dotnet-6.png](https://sahansera.dev/static/649b50c189801c60e912a81c69d4651a/5a190/building-grpc-server-dotnet-6.png "building-grpc-server-dotnet-6.png")

gRPC template uses TLS by default and Kestrel doesn’t support HTTP/2 with TLS on macOS systems. We need to turn off TLS (ouch!) in order for our demo to work.

> 💡 Please don’t do this in production! This is intended for local development purposes only.

On local development

```csharp
// Turn off TLS
builder.WebHost.ConfigureKestrel(options =>
{
  // Setup a HTTP/2 endpoint without TLS.
  options.ListenLocalhost(5000, o => o.Protocols =
      HttpProtocols.Http2);
});
```

## Testing the service

Usually, when interacting with the HTTP/1.1-like server, we can use cURL to make requests and inspect the responses. However, with gRPC, we can’t do that. (you can make requests to HTTP/2 services, but those won’t be readable). We will be using [gRPCurl](https://github.com/fullstorydev/grpcurl) for that.

Once you have it up and running, you can now interact with the server we just built.

```sh
grpcurl -plaintext localhost:8080 Inventory/GetBookList
```

<aside>
💡 Note: gRPC defaults to TLS for transport. However, to keep things simple, I will be using the `-plaintext` flag with `grpcurl` so that we can see a human-readable response.
</aside>

How do we figure out the endpoints of the service? There are two ways to do this. One is by providing a path to the proto files, while the other option enables reflection through the code.

**Using proto files**

If you don’t want to enable reflection, we can use the Protobuf files to let gRPCurl know which methods are available. Normally, when a team makes a gRPC service they will make the protobuf files available if you are integrating with them. So, without having to ask them or doing trial-and-error you can use these proto files to introspect what kind of endpoints are available for consumption.

```sh
grpcurl -import-path Proto -proto bookshop.proto -plaintext localhost:5000 Inventory/GetBookList
```

![building-grpc-server-dotnet-7.png](https://sahansera.dev/static/b0011f4a15506033cf4ed87954a32778/5a190/building-grpc-server-dotnet-7.png "building-grpc-server-dotnet-7.png")

Now, let’s say we didn’t have reflection enabled and try to call a method on the server.

```sh
grpcurl -plaintext localhost:5000 Inventory/GetBookList
```

We can expect that it will error out. Cool!

![building-grpc-server-dotnet-8.png](https://sahansera.dev/static/3b54de060442b6ebe32148fd3cea900e/5a190/building-grpc-server-dotnet-8.png "building-grpc-server-dotnet-8.png")

**Enabling reflection**

While in the `BookshopServer` folder run the following command to install the reflection package.

```sh
dotnet add package Grpc.AspNetCore.Server.Reflection
```

Add the following to the Program.cs file. Note that we are using the new Minimal API approach to configure these services

```csharp
// Register services that enable reflection
builder.Services.AddGrpcReflection();

// Enable reflection in Debug mode.
if (app.Environment.IsDevelopment())
{
  app.MapGrpcReflectionService();
}
```

![building-grpc-server-dotnet-9.png](https://sahansera.dev/static/1490dc3c85335a827f86af33132fe227/5a190/building-grpc-server-dotnet-9.png "building-grpc-server-dotnet-9.png")

## Conclusion

As we have seen, similar to the Go implementation, we can use the same Protocol buffer files to generate the server implementation in .NET. In my opinion .NET’s new tooling makes it easier to generate the server stubs when a change happens in your Protobufs. However, setting up the local developer environment could be a bit challenging especially for macOS.

Feel free to let me know if you have any questions or feedback. Until next time! 👋

## References

- [https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-6.0&tabs=visual-studio-code](https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-6.0&tabs=visual-studio-code)
- [https://grpc.io/docs/languages/csharp/quickstart/](https://grpc.io/docs/languages/csharp/quickstart/)
- [https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-6.0#unable-to-start-aspnet-core-grpc-app-on-macos](https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-6.0#unable-to-start-aspnet-core-grpc-app-on-macos)
- [https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60-