Overview

Today, efficiency, scalability, and interoperability are paramount in modern software development. Enter gRPC, Google's open-source framework for RPCs (Remote Procedure Calls). In this article, we'll delve into the fundamentals of gRPC and explore how it integrates seamlessly with .NET 8 and C# 12 to empower developers in building robust, distributed systems.

What is gRPC?

The gRPC RPC framework, which stands for "gRPC Remote Procedure Call," is a language-agnostic, modern RPC framework that uses HTTP/2 for transport and Protocol Buffers (protobuf) for interface definition. There are several advantages to using gRPC over traditional HTTP APIs, including:

Key Components of gRPC

Service Definition

gRPC services are defined using Protocol Buffers (protobuf), which define methods that can be called remotely.

Understanding Protocol Buffers (Protobuf)

Protocol Buffers (Protobuf) are the basis of gRPC. They are a language-neutral, platform-neutral, extensible method for serializing structured data. They are smaller, faster, and easier to use than XML or JSON.

Using Protobuf, we define gRPC services and the structure of requests and responses. This definition is contained in .proto files.

Defining gRPC Services

The .proto file contains the definitions of the services and the messages they use. Each service definition specifies the remote procedure calls (RPCs) that the service supports.

We will be creating a folder named Protos in the root folder where the solution file is kept, and in that folder, we will create a file named greet.proto. Since we will be using the Protos folder and greet.proto for both the client and the server, we are creating them in the root folder.

Inside the greet.proto file please copy the code example I have given we below and save it.


syntax = "proto3";

option csharp_namespace = "GreeterServer";

package greet;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

In this example above we have the:

Remote Procedure Calls (RPCs)

There are several RPCs in the service definition. The above example defines a simple unary RPC.

Generating Code from Protobuf

It is necessary to generate the corresponding code once we have defined the service in the .proto file. The gRPC tools for .NET automatically generate C# classes from the Protobuf definitions, which include:

In .NET projects, this is usually handled automatically by the build process using tools like Grpc.Tools, which use the protoc compiler with the appropriate gRPC plugin.

Implementing the Service

Using the generated code, we extend the generated base classes to implement the Greeter service. As the code example below shows us.

using Grpc.Core;
namespace GreeterServer.Services;
public class GreeterService : Greeter.GreeterBase
{
    private readonly ILogger<GreeterService> _logger;

    public GreeterService(ILogger<GreeterService> logger)
    {
        _logger = logger;
    }

    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        try
        {
            var reply = new HelloReply
            {
                Message = $"Hello, {request.Name}"
            };
            _logger.LogInformation($"GRPC Request Send and Recived");
            return Task.FromResult(reply);
        }
        catch (Exception ex)
        {
            _logger.LogError($"Error while handing your request {ex.Message}");
            throw;
        }
        
    }
}

As a result, this implementation extends the generated GreeterBase class. The SayHello method has been overridden to implement the SayHello RPC on the server side.

Using the Service in a Client Application

We invoke the RPC methods from a client application by creating a gRPC channel and a client stub.

We will need to install the following NuGet Packages

using GreeterServer;
using Grpc.Net.Client;

var channel = GrpcChannel.ForAddress("https://localhost:7273");
var client = new Greeter.GreeterClient(channel);
Console.WriteLine("Hello from Ziggy Rafiq");
Console.WriteLine("Please Enter Your Name:");
string name = Console.ReadLine()?? "Missing Your Name !!";
var response = await client.SayHelloAsync(new HelloRequest{ Name = name });
Console.WriteLine(response);
Console.ReadLine();

In this client code:

In .NET, defining services and messages using Protobuf is a fundamental component of gRPC. By defining these services and messages in .proto files, we can leverage gRPC's powerful features to build efficient, scalable, and cross-platform APIs. In order to perform remote procedure calls, client applications must define services and messages, generate code, and implement the service logic.

gRPC in .NET 8 with C# 12

The .NET 8 framework now supports gRPC natively, making it easier than ever for .NET developers to get started using this modern RPC framework.

Setting Up a gRPC Service

We go to our Visual Studio 2022 click on Create a new project and we search for gRPC template name ASP.net Core gPRC service. This will create for us a boilerplate project which has the following setup for our gRPC Server.

We have the following folders and files

  1. Navigate to "Connected Services" by right-clicking on "Add" and selecting "Connected Service."
  2. In the "Connected Services" dialog, choose "Service References (OpenAPI, gRPC, WCF Web Service)" from the second section.
  3. Click the green plus icon, select "gRPC," and click "Next."
  4. Choose the radio button for "File," then click the browse button.
  5. Navigate to the "Protos" folder in the root directory (not the project folder) and select the "greet.proto" file.
  6. Ensure to select "Server" from the drop-down list to connect with your proto file.

Implementing the Server

We now go to our project name GreeterServer and we go to the folder name Services and open up the file name GreeterService.cs and we copy the following code below.

using Grpc.Core;
namespace GreeterServer.Services;
public class GreeterService : Greeter.GreeterBase
{
    private readonly ILogger<GreeterService> _logger;

    public GreeterService(ILogger<GreeterService> logger)
    {
        _logger = logger;
    }

    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        try
        {
            var reply = new HelloReply
            {
                Message = $"Hello, {request.Name}"
            };
            _logger.LogInformation($"GRPC Request Send and Recived");
            return Task.FromResult(reply);
        }
        catch (Exception ex)
        {
            _logger.LogError($"Error while handing your request {ex.Message}");
            throw;
        }
        
    }
}

Make sure the following NuGet Packages are installed on the Server project. By default it should installed however there are times when it is not.

We have now created a simple gRPC Server Project and we build it and now we look at building the gRPC client project.

Creating a gRPC Client

In this section we will create a simple Console Application and install the following NuGet Packages in our Console Application.

We also need to create a folder name Protos and follow the following steps below to add the reference to the greet.proto file.

  1. Navigate to "Connected Services" by right-clicking on "Add" and selecting "Connected Service."
  2. In the "Connected Services" dialog, choose "Service References (OpenAPI, gRPC, WCF Web Service)" from the second section.
  3. Click the green plus icon, select "gRPC," and click "Next."
  4. Choose the radio button for "File," then click the browse button.
  5. Navigate to the "Protos" folder in the root directory (not the project folder) and select the "greet.proto" file.
  6. Ensure to select "Server" from the drop-down list to connect with your proto file.

We will now copy the code below that I have given here which will allow us to connect to the gRPC Server and we then need to set up our solution to run multiple projects selecting both of our projects GreeterServer and GreeterClient.

using GreeterServer;
using Grpc.Net.Client;

var channel = GrpcChannel.ForAddress("https://localhost:7273");
var client = new Greeter.GreeterClient(channel);
Console.WriteLine("Hello from Ziggy Rafiq");
Console.WriteLine("Please Enter Your Name:");
string name = Console.ReadLine()?? "Missing Your Name !!";
var response = await client.SayHelloAsync(new HelloRequest{ Name = name });
Console.WriteLine(response);
Console.ReadLine();

Benefits of Using gRPC

Here are some of the key advantages of gRPC that make it an appealing choice for building efficient, scalable, and robust distributed systems:

Performance

Binary Serialization

Binary serialization with Protocol Buffers (Protobuf) is one of the unique features of gRPC. Binary serialization is more compact and faster to parse than text-based serialization formats like JSON or XML. Therefore, payload sizes will be reduced and communication between services will be faster.

Compact Message Format: A protobuf message is smaller because it is serialized in a binary format, which eliminates the need for verbose tags and data representations used in text-based formats.

Faster Parsing: Compared to JSON or XML, the binary format allows for faster serialization and deserialization since it requires fewer computational resources.

HTTP/2 Multiplexing

The HTTP/2 transport protocol used by gRPC offers several performance advantages over HTTP/1.1.

gRPC's performance optimizations make it an excellent choice for high-performance, low-latency communication in microservice architectures and other distributed systems.

Strongly Typed Contracts

Protocol Buffers (Protobuf)

In gRPC, the service contracts are defined using Protocol Buffers (Protobuf), which provides type safety, maintainability, and clarity.

Example of a .proto file:

syntax = "proto3";

option csharp_namespace = "GreeterServer";

package greet;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Interoperability

Multi-language Support

This allows teams to choose the best tools and languages for their specific needs without worrying about integration concerns. gRPC supports a wide range of programming languages.

In terms of performance, strongly typed contracts, and interoperability, gRPC is a great choice for building modern, efficient, and scalable distributed systems. Using binary serialization and HTTP/2, gRPC ensures fast, resource-efficient communication. Protocol Buffers ensure that service definitions are clear, maintainable, and type-safe. Finally, its language-agnostic nature allows for seamless integration across different programming environments, allowing teams to choose the best tools for their specific needs. Combined, these features contribute to the increasing adoption of gRPC for APIs and microservices.

Summary

Modern, scalable microservices and distributed systems can be built using gRPC in .NET 8 and C# 12. In addition to its efficient communication, strong typing, and cross-language support, it is an ideal choice for developers seeking to optimize performance and interoperability.

Take advantage of the power of gRPC and unleash new possibilities in our .NET applications regardless of whether we explore microservices architecture or enhance our existing distributed systems.

I have uploaded this article's source code on my GitHub Repository https://github.com/ziggyrafiq/grpc-dotnet8-csharp12 please do not forget to follow me on LinkedIn https://www.linkedin.com/in/ziggyrafiq/ as your support means the world to me and if you have found this article useful please click the like button also I will be writing further articles on gRPC with .net with more complex examples and will be sharing with you best practices and standards in building the gRPC services and will be explaining in detail where we can use them and how.