gRPC for C# (.NET): libraries and examples
gRPC is a modern high-performance remote procedure call (RPC) framework developed by Google. It uses HTTP/2 for transport and Protocol Buffers (protobuf) for data serialization. In the .NET ecosystem, gRPC has become a primary tool for building microservice architectures, providing strong typing, bidirectional streaming, and low latency. In this article, we will review the key libraries for working with gRPC in C# and provide practical examples.
Main libraries for gRPC in .NET
The following NuGet packages are used for working with gRPC in .NET:
- Grpc.Net.Client — client library for creating gRPC clients. It works on top of HttpClient and supports all call types: unary, server streaming, client streaming, and bidirectional streaming.
- Grpc.AspNetCore.Server — server library for integrating gRPC into ASP.NET Core. It allows hosting gRPC services alongside regular REST APIs.
- Google.Protobuf — library for working with Protocol Buffers, generating and deserializing messages.
- Grpc.Tools — a set of tools for automatically generating C# code from .proto files during project build.
The development process includes three stages: describing the service in a .proto file, generating code, and implementing the server/client.
Example 1: Simple unary service (request-response)
Let's create a simple service for retrieving user information. To do this, define the file user.proto:
syntax = "proto3";
package user;
service UserService { rpc GetUser (UserRequest) returns (UserResponse);}
message UserRequest { int32 id = 1;}
message UserResponse { string name = 1; string email = 2; int32 age = 3;}Add package references and the .proto file to the project. After building, the classes UserRequest, UserResponse, and the base class UserServiceBase will be generated.
Server implementation
using Grpc.Core;using User;
public class UserServiceImpl : UserService.UserServiceBase{ public override Task<UserResponse> GetUser(UserRequest request, ServerCallContext context) { var user = new UserResponse { Name = "Ivan Petrov", Email = "ivan@example.com", Age = 30 }; return Task.FromResult(user); }}Starting the server in ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);builder.Services.AddGrpc();var app = builder.Build();app.MapGrpcService<UserServiceImpl>();app.Run();Client implementation
using Grpc.Net.Client;using User;
var channel = GrpcChannel.ForAddress("https://localhost:5001");var client = new UserService.UserServiceClient(channel);
var response = await client.GetUserAsync(new UserRequest { Id = 1 });Console.WriteLine($"User: {response.Name}, Email: {response.Email}");Example 2: Server streaming
Server streaming is useful when you need to send a large amount of data to the client, such as a list of events or logs. Let's extend our .proto file:
service UserService { rpc GetUser (UserRequest) returns (UserResponse); rpc ListUsers (Empty) returns (stream UserResponse);}
message Empty {}Server implementation:
public override async Task ListUsers(Empty request, IServerStreamWriter<UserResponse> responseStream, ServerCallContext context){ var users = new List<UserResponse> { new UserResponse { Name = "Anna", Email = "anna@test.com" }, new UserResponse { Name = "Boris", Email = "boris@test.com" } };