gRPC in DotNetCore from scracth
Mohsen Kokabi
Posted on February 10, 2020
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
services.AddGrpc();
In Configure method, in UseEndpoints block add:
endpoints.MapGrpcService<CalcService>();
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:
<ItemGroup>
<Protobuf Include="Protos\calc.proto" GrpcServices="Server" />
</ItemGroup>
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
});
}
}
Client
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:
<ItemGroup>
<Protobuf Include="Protos\calc.proto" GrpcServices="Client" />
</ItemGroup>
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(addreply.C);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
This time we are using the partial class CalcClient nested in Calc class.
Running
Remember before running the server you need to trust to the certificate:
dotnet dev-certs https --trust
The code can be found in (github)[https://github.com/mkokabi/gRPC]
Posted on February 10, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.