gRPC and .NET Core

Introduction

In my previous post (found below) I’ve explained what gRPC is, how it looks compared to the standard RESTful approach, its pros and cons, and what is the best use for it.

Now, let’s take a dive into the code and see how very basic implementation of gRPC Client and Service can look like in .NET Core.

Prequisites

To follow this guide you’ll need:

You can also use Visual Studio Code easily, but you’ll have to use command-line and modify manually your *.csproj files.

If you’re limited to VS Code or Rider, please use the following guide:
https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-3.1&tabs=visual-studio-code

Creating Solution & Project

If you’ve got everything in place, you should be able to find gRPC type of project in Visual Studio and use it. Doing this, you’ll have an opportunity to generate basic Client and/or Service with a sample implementation.

gRPC Project Type in Visual Studio

But well, we won’t take this shortcut and we will create everything from “scratch”. First, let’s create an empty ASP.NET Core project.

ASP.NET Project
Picking new ASP.NET Core project

Fill desired project and solution name (not relevant in this case) and now, since we want to add all necessary components from scratch, let’s pick Empty template:

Empty ASP.NET Core Project
No shortcuts, EMPTY all the way!

Adding gRPC NuGet package

Now, having an empty project, let’s add gRPC NuGet package reference. Version might vary, but there shouldn’t be any major differences as for the time of this writing:

gRPC NuGet package for .NET Core
Official gRPC NuGet package from gRPC authors

Adding Protocol Buffer

As we should know from my Introduction to gRPC post, Protocol Buffer files, known as Protobuf are defining our Service – its operations and payloads.

Let’s create *.proto file in Protos/ folder of our project. I will name it order.proto, as it will be responsible for sample order creation. Our Protobuf will look as below:

// Protocol Buffer version
syntax = "proto3";

// Namespace declaration for C# compiler, where service code will be generated
option csharp_namespace = "Example.Grpc.Service.Services";

// Service definition with operations, parameters and returned types
service Ordering {
    rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
}

// Operation types below
message CreateOrderRequest {
    Order order = 1;
}

message CreateOrderResponse {
    string orderId = 1;
}

message Order {
	string id = 1;
	string customerName = 2;
	double value = 3;
	repeated OrderItem items = 4; // 'repeated' keyword declares collections
}

message OrderItem {
	string id = 1;
	string name = 2;
}

As it can be easily noticed, we’ll have one Service – Ordering, which is exposing one RPC operation – CreateOrder. Furthermore, we declared payload models for that operation.

Adding Service Reference

Having the file, we need to reference it properly to let the compiler know, that this is Protobuf definition of our Service. We can achieve it by adding it as a connected service as shown below.

Picking Add Connected Service in VS
Adding Connected Service from Dependencies context-menu in Visual Studio
Choosing gRPC service reference
Picking gRPC as a type of the service

As the last step, we have to choose location of *.proto file as well as type of class to generate. In this example, this will be a local Protobuf file.

Choosing *.proto file and type of generated class for the service
Location of Protobuf file and type of generated class

And we’re done, we should have our Protobuf properly referenced as a definition of the Service. Now after each compilation, Protocol Buffer Compiler should generate base abstract class based on that definition.

We can also achieve the same by i.e. editing project *.csproj file. After all steps above, it should look as shown below:

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

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Remove="Protos\order.proto" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.31.0" />
  </ItemGroup>

  <ItemGroup>
    <Protobuf Include="Protos\order.proto" GrpcServices="Server" />
  </ItemGroup>

</Project>

Implementing the Service

Autogenerated code for our base class should be placed in the namespace declared in csharp_namespace option of our Protobuf file – in our case is “Example.Grpc.Service.Services“.

All we need to do now is to create a derived class that will override RPC methods of its abstract base definition.

NOTE: In the implementation below you can notice an injection of repository and usage of model classes, which both are missing in this guide. For full implementation of the Service as well as the Client please refer to my example in GitHub repository:
https://github.com/swiftybathero/Example.Grpc

Example gRPC Service implementation

Notice that our class inherits from Ordering.OrderingBase, which was generated by the Protocol Buffer Compiler. Name Ordering comes from our Service definition in our Protobuf file.

This abstract class contains abstract methods corresponding to our RPC operations. In this case, we are overriding one method – CreateOrder.

In the example code above, we’re creating an Order based on incoming payload and returning its Id once the Order is created.

Startup.cs

Startup.cs in ASP .NET Core is responsible for runtime configuration of application dependencies as well as other configurations, like routing.

Firstly, we have to register our gRPC Service. All services that we have will be discovered automatically during runtime and registered in IoC container.

public void ConfigureServices(IServiceCollection services)
{
    // Registration of gRPC services
    services.AddGrpc();
}

Then, we have to configure routing for our service. Go to Configure method of Startup.cs and add the following to endpoint configuration:

// Registering endpoints for gRPC service
endpoints.MapGrpcService<OrderingService>();

We’re ready to go. Our Service should be ready to launch. Let’s create the client.

Adding and implementing the Client

Create a project for our Client implementation. In this case, it will be .NET Core Console App (pretty obvious for example code demonstration  🙂)

Adding Service Reference to the Client

To consume the service the Client needs to know about its contract. And so, we have to add a service reference to the Protobuf file, that’s defined by the gRPC Service.

This will allow Protocol Buffer Compiler to generate the client code. In our client application follow the same steps as in adding a reference in the service, but this time select the Client type of class instead as shown below.

Choosing *.proto file and type of generated class for the client
Adding reference to Protobuf file in Client application

This operation will also add all required gRPC NuGet packages.

Automatic addition of gRPC NuGet packages on Client generation
All required NuGed packages are added automatically

Calling the Service

After we properly referenced Protobuf file, C# compiler should be able to generate the client code. Now, the only thing left is to use it.

Firstly, we have to create a gRPC channel, which is an abstraction for connections to remote servers. Since it’s an expensive operation compared to invoking a remote all, we should reuse a single channel for as many calls as possible. gRPC channel should have an address of the Service – this may vary depending on your service configuration.

Created channel should be used to instantiate the client.

// Creating channel and client
using var channel = GrpcChannel.ForAddress(serviceUrl);
var client = new Ordering.OrderingClient(channel);

Having the client, the only thing left is to invoke RPC operation declared in Service contract.

var createOrderResponse = await client.CreateOrderAsync(createOrderRequest);

Our full implementation of the main method in client code can look as below.

Example usage of gRPC Client

Summary

A basic implementation of gRPC Clients and Services in .NET Core is very simple. Once we have our Protobuf file created, all we need is to reference it properly in our project files. Once it’s done, C# and Protocol Buffer Compiler are doing most of the job for us, by generating base class for the Service and typed Client for client applications.

More to read


Paweł Klatt
Paweł Klatt

Full-Stack .NET Developer with over 10 years of professional experience.
Fan of React and .NET Core.