DEV Community

Mohsen Kokabi
Mohsen Kokabi

Posted on

gRPC in DotNetCore from scracth

Why gRPC

Firstly, gRPC is based on HTTP/2. Thus, we might first say what are the key features of HTTP/2

  • Binary protocol
  • Streams
  • Request multiplexing over single TCP connection

Now we can say on top of HTTP/2 features, gRPC provides:

  • Performance: coming from binary protocol and multiplexing
  • Interoperability
  • Streaming
  • Deadline/timeout and cancellation: Both client and server can define a timeout. Furthermore, client can abort an operation earlier if necessary.
  • Security

gRPC uses a binary based format called Protocol Buffer.

Creating the Server

Dotnet Core and Visual Studio both have a template to create a gRPC server, but in this tutorial, I am going to create it from scratch. In addition, we are going to create the client.

md gRPC
cd .\gRPC\

dotnet new web -o Server
cd .\Server\
dotnet build 
dotnet add .\Server.csproj package Grpc.AspNetCore

Note: The reason for making a build at this stage is to have the bin\Debug\netcoreapp3.1 folder created which would be used by the protoc.

Open the project folder in Code. In ConfigureServices method add


In Configure method, in UseEndpoints block add:


Now we need to add the CalcService. We can create a folder called Services and put our CalcService there.
For now it can be just an empty class.

Adding protos

Add a Protos folder and create a "calc.proto" inside that.

Edit the csproj file and add:

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

The content of proto would be:

syntax = "proto3";

option csharp_namespace = "Server";

package calc;

service Calc {
  rpc Add (AddRequest) returns (AddResponse);

message AddRequest {
    int32 a = 1;
    int32 b = 2;

message AddResponse {
    int32 c = 1;

Note 1: The first line is mandatory.
Note 2: the namespace would be used in the generated C# code.
Note 3: the values in front of the a, b and c are their field number for proto format. They should be positive and unique in that message object. The reason is proto format, unlike XML and json doesn't serialize the property names and instead works based on the field number.
Note 4: There would be one class created for each message (AddRequest and AddResponse)

Compiling protos

You need to first Download the protoc for your operating system from their release repository.
After extracting you would find the protoc in the bin folder.

protoc.exe --csharp_out=.\bin\Debug\netcoreapp3.1 .\Protos\calc.proto
dotnet build .

Using the classes created by protoc

There would be a abstract partial class called CalcBase in CalcGrp.cs. The CalcService would override the Add method of the CalcBase. The full code would be like:

    public class CalcService : CalcBase {
        public async override Task<AddResponse> Add(AddRequest request, ServerCallContext context) 
            return await Task.FromResult(new AddResponse
                C = request.A + request.B


If you are in the Server folder go one level up and start creating a console application

dotnet new console -o Client
cd .\Client\
dotnet build
dotnet add .\Client.csproj package  Grpc.Net.Client
dotnet add .\Client.csproj package  Google.Protobuf
dotnet add .\Client.csproj package  Grpc.Tools

Note: Again, we have build the project early to have the bin\Debug\netcoreapp3.1 folder created so protoc tool can write its output there.

Now we need to copy the Protos folder and its contents from the server project:

md Protos
Copy ..\Server\Protos\* .\Protos

Edit your csproj file and add:

    <Protobuf Include="Protos\calc.proto" GrpcServices="Client" />

Note: If you are copying from the server application, remember to change the GrpcServices to Client.
Edit the proto file and change the namespace to Client.

option csharp_namespace = "Client";

Now create the proxy files using protoc.

protoc.exe --csharp_out=.\bin\Debug\netcoreapp3.1 .\Protos\calc.proto

In the program edit the main method to:

        static async Task Main(string[] args)
            using var channel = GrpcChannel.ForAddress("https://localhost:5021");

            var calcClient = new Calc.CalcClient(channel);
            var addreply = await calcClient.AddAsync(
                new AddRequest { A = 2, B = 3 }

            Console.WriteLine("Press any key to exit...");

This time we are using the partial class CalcClient nested in Calc class.


Remember before running the server you need to trust to the certificate:

dotnet dev-certs https --trust

The code can be found in (github)[]

Discussion (0)