avatarGabriele Tronchin

Summary

This article provides a comprehensive guide on implementing gRPC client and server in .NET 8, including the basics, project examples, and code snippets, while highlighting gRPC's performance, language neutrality, and streaming capabilities.

Abstract

The article "How to Implement gRPC Client and Server in .NET 8" delves into the intricacies of gRPC, an advanced RPC framework developed by Google, known for its high performance and support for multiple languages. It outlines the advantages of using gRPC, such as its high-speed communication, contract-first approach with Protocol Buffers, and robust streaming support. The author illustrates the practical application of gRPC in microservices architectures, polyglot environments, and real-time services, providing a link to the official gRPC website and C#/.NET documentation for further reading. A sample project by Gabriele Tronchin is shared on GitHub, demonstrating CRUD operations through two services: a gRPC client and server API. The article walks through defining a proto file, importing it into a .NET project, and implementing the server and client-side logic. It also touches on using Mapster for object-to-object mapping and references Microsoft's official documentation for additional guidance.

Opinions

  • The author posits that gRPC represents a pinnacle in RPC frameworks due to its advanced performance and language neutrality.
  • gRPC's efficient serialization using Protocol Buffers is emphasized as a key feature that minimizes network overhead.
  • The article suggests that gRPC is ideally suited for microservices architectures, polyglot environments, and real-time services.
  • The use of a practical project example and code snippets indicates the author's belief in the importance of hands-on learning and the value of concrete examples in understanding complex topics.
  • The mention of Mapster as a tool for automating object mapping implies a recommendation for its use in streamlining development with gRPC in .NET.
  • By providing links to official resources and documentation, the author conveys the importance of referencing authoritative sources for in-depth understanding and best practices.

How to Implement gRPC Client and Server in .NET 8

gRPC, born from Google’s innovation, represents a pinnacle in remote procedure call (RPC) frameworks, excelling in performance and language neutrality.

In this article, we’ll explore how to implement gRPC in ASP.NET Core.

We’ll cover the basics, provide a project example, and even share code snippets to help you get started.

About gRPC

gRPC stands as a contemporary, open-source RPC (Remote Procedure Call) framework, celebrated for its high performance and versatility.

Advantages of gRPC:

  • High-Speed RPC Framework: Offers advanced performance and agility in communication.
  • Contract-First Approach: Leverages Protocol Buffers for API development, enabling language-agnostic implementations.
  • Streaming Support: Facilitates seamless streaming communication for clients, servers, and bidirectional scenarios.
  • Efficient Serialization: Minimizes network overhead through the binary serialization of Protobuf.

gRPC finds ideal application in:

  • Microservices Architectures: Prioritizes efficiency and streamlined communication.
  • Polyglot Environments: Enables development across multiple programming languages.
  • Real-Time Services: Seamlessly handles streaming requests and responses.

Official Resources:

Implementation

Here’s a link to a project I’ve prepared :

This project presents a practical implementation of gRPC communication, featuring two distinct services:

  1. Sample.GRPC.Client.API: This service encapsulates CRUD operations, leveraging a gRPC client to interact with the underlying Sample.GRPC.Server.API.
  2. Sample.GRPC.Server.API: Operating as a gRPC server, this service facilitates CRUD operations. For testing purposes, an in-memory database has been utilized.

Proto File

The initial step involves defining a proto file, within which we outline our gRPC service methods, requests, and response models.

Here’s a segment of the proto file found within this project:

syntax = "proto3";

import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";

option csharp_namespace = "SampleServiceProto";

package sampleservice;

service SampleServiceApi {
  rpc Gets (Empty) returns (responseEntitiesModel);
  rpc GetSingle (entityRequest) returns (responseEntityModel);
  rpc Create (CreationRequest) returns (operationCompleteModel);
  rpc Update (UpdateRequest) returns (operationCompleteModel);
  rpc Delete (entityRequest) returns (responseModel);
}

message entityModel {
  string id = 1;
  string name = 2;
  string description = 3;
  google.protobuf.Timestamp referenceDate = 4;
  google.protobuf.Timestamp modifiedDate = 5;
}

// Message and service definitions follow...

Key points:

  • csharp_namespace: Specifies the namespace you can utilize within your C# code to reference the models defined in the proto file.
  • service SampleServiceApi: Defines the service name and the methods it provides.
  • entityModel: Provides a sample definition of an entity.

For further details about the syntax:

Server Side Implementation

The server-side implementation demonstrates efficient handling of CRUD operations.

After defining a proto file, it must be imported into our .NET project, with the type of Server specified.

Here’s a sample excerpt from the project reference:

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

Following the import of the proto file, a class is defined that inherits from SampleServiceApi.

Inside this class, the methods defined for the service in the proto file are overridden to provide custom implementation.

public class GrpcSampleService : SampleServiceApi.SampleServiceApiBase
{
  public override async Task<responseEntitiesModel> Gets(Empty request, ServerCallContext context)
  {
      try
      {
          var lst = await dbContext.SampleEntities.ToListAsync();

          var response = new responseEntitiesModel() { Success = true };
          response.Items.AddRange(lst.Adapt<IEnumerable<entityModel>>());

          return response;
      }
      catch (Exception ex)
      {
          logger.LogError(ex, "An error occurred at {Gets}", nameof(Gets));

          var errorRetModel = new responseEntitiesModel() { Success = false };
          errorRetModel.Exceptions.Add(new apiException() { Message = ex.Message, StatusCode = 500 });
          return errorRetModel;
      }
  }


    // Other Method implementations...
}

Client Side Implementation

The client-side implementation provides a seamless interface for interacting with the server.

After defining a proto file, it must be imported into our .NET project, with the type of Client specified.

Here’s a sample excerpt from the project reference:

 <Protobuf Include="GRPCClient\sampleservice.proto" GrpcServices="Client" />

Within the client project, a Service called ServiceClientGrpc is defined to wrap the actual GRPC client.

This allows for encapsulation of all dependencies to gRPC within this service.

private readonly SampleServiceApi.SampleServiceApiClient _client;

// Other code omitted for brevity

public class ServiceClientGrpc : IServiceClientGrpc
{
      public async Task<IEnumerable<SampleEntityGet>> Get()
      {
          var response = await _client.GetsAsync(new Empty(), _metadata);

          if (response.Success)
          {
              return response.Items.Adapt<IEnumerable<SampleEntityGet>>();
          }

          throw new InvalidOperationException(string.Join(',', response.Exceptions.Select(x => x.Message)));
      }

      // Other code omitted for brevity
}

Other Considerations

Within this project, Mapster is employed to automate the mapping of gRPC models to C# classes.

For some classes, custom mappers are defined, as illustrated below:

  TypeAdapterConfig<entityModel, SampleEntityGet>.NewConfig()
  .Map(dest => dest.Id, src => Guid.Parse(src.Id))
  .Map(dest => dest.Name, src => src.Name)
  .Map(dest => dest.Description, src => src.Description)
  .Map(dest => dest.ReferenceDate, src => src.ReferenceDate.ToDateTime())
  .Map(dest => dest.LastTimeModified, src => src.ModifiedDate.ToDateTime()); 


  TypeAdapterConfig<SampleEntityPost, CreationRequest>.NewConfig()
  .Map(dest => dest.Item.Name, src => src.Name)
  .Map(dest => dest.Item.Description, src => src.Description)
  .Map(dest => dest.Item.ReferenceDate, src => Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(src.ReferenceDate));

At this point, the .Adapt method can be used to convert gRPC models to API models.

response.Items.Adapt<IEnumerable<SampleEntityGet>>();

Microsoft Official Documentation

For further exploration and detailed guidance, refer to Microsoft’s official documentation:

Grpc
Csharp
Dotnet
Dotnet Core
Recommended from ReadMedium