DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Playing with gRPC and .NET 6
  • Implementing API Design First in .NET for Efficient Development, Testing, and CI/CD
  • Docs That Write Themselves: Scaling With gRPC and Protobuf
  • Database Query Service With OpenAI and PostgreSQL in .NET

Trending

  • Mastering Advanced Traffic Management in Multi-Cloud Kubernetes: Scaling With Multiple Istio Ingress Gateways
  • Designing AI Multi-Agent Systems in Java
  • Power BI Embedded Analytics — Part 3: Power BI Embedded Demo
  • Assessing Bias in AI Chatbot Responses

Playing With gRPC and .NET 6: Client Side

Create a gRPC client in .NET.

By 
moises zapata user avatar
moises zapata
·
Sep. 07, 22 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
6.3K Views

Join the DZone community and get the full member experience.

Join For Free

In my previous article, I focused on how we can create a gRPC server using .NET. Now, I want to show you how we can create a gRPC client using .NET.

In the next image, I mark the items that we will focus on this tutorial.

Prerequisites

I'm using macOS, and the next commands are specific to this OS:

  • gRPC compiler: To install Protobuf compiler, you can execute this in a terminal:
brew install protobuf
  • .NET 6 SDK: Here you can find the links to download and install the .NET 6 SDK
  • Visual Studio Code or IDE of your choice
  • grpcurl: A command-line tool that provides interaction with gRPC services
brew install grpcurl
  • grpcui: builds on top of gRPCurl and adds an interactive web UI for gRPC, similar to tools like Postman and Swagger UI
brew install grpcui

Steps

In a general manner, we should follow the next steps:

  1. Create a project.
  2. Add the gRPC dependencies to the project.
  3. Add the proto file on your project.
  4. Register the created proto file and compile the project.
  5. Implement the business logic.
In this example, we will use a .NET project called CountryGrpcClient that calls to the server to search, create or get a list of countries.  The CountryGrpc.proto file (described below) declares the remote procedures.

1. Create a Project

To create a gRPC template .NET project, open a terminal and execute the next command:

 
dotnet new console -o grpc.country.client -n CountryGrpcClient

The output is something like this:

Here:

  • -o parameter is used to define the project directory name: grpc.country.client.
  • -n parameter is used to define the project name: CountryGrpcClient.

2. Add the gRPC Dependencies to the Project

 
dotnet add CountryGrpcClient.csproj package Grpc.Net.Client --version '2.47.0'
dotnet add CountryGrpcClient.csproj package Grpc.Tools --version '2.47.0'
dotnet add CountryGrpcClient.csproj package Google.Protobuf --version '3.21.5'

You can see that the entry ItemGroup in your CountryGrpcClient.csproj has changed to something like this:

3. Add the Proto File in the Project

In your project directory, create a folder called Protos with a file CountryGrpc.protos in it. 

 
mkdir ./Protos
touch ./Protos/CountryGrpc.proto

In this step, I'll use the same proto file created in the previous article in which we created a gRPC server.

Copy the next lines in the CountryGrpc.proto file created previously.

ProtoBuf
 
syntax = "proto3";

/*The Proto file that has Empty message definition*/
import "google/protobuf/empty.proto";

// Defining the namespace in which the generate classes will be 
option csharp_namespace = "Sumaris.Grpc.Services";

// The service name will be used by the compiler when generate the base classes
// Here I declare five procedure
service CountryService{
	//Server streaming RPC
    rpc getAllCountries(google.protobuf.Empty)
        returns (stream Country);
	// Unitary RPC
    rpc listAllCountries(google.protobuf.Empty)
        returns ( CountryList);
    // Unitary RPC
    rpc findCountryByName( FindCountryByNameRequest )
        returns (FindCountryByNameResponse);
	// Unitary RPC
    rpc createCountry (CountryCreateRequest)
        returns (CountryCreateRespopnse);
	// Bidrectional streaming RPC
    rpc findCountriesByNames( stream FindCountryByNameRequest)
        returns (stream Country);
}


message Country{
    string name=1;
    string capitalCity=2;
    float area=3;
}

message CountryList{repeated Country country = 1;}

message FindCountryByNameRequest{string name=1;}

message FindCountryByNameResponse{Country country=1;}

message CountryCreateRequest{ Country country=1;}

message CountryCreateRespopnse{bool created=1;}

4. Register the Created Proto File and Compile the Project

Add the next lines in your configuration project file, CountryGrpcClient.csproj:

XML
 
<ItemGroup>
    <Protobuf Include="./Protos/CountryGrpc.proto" GrpcServices="Client" />
</ItemGroup>

Open a terminal, move into the project, and execute the next command to compile the project:

 
dotnet build

 The build process creates two files, CountryGrpc.cs and CountryGrpcGrpc.cs, in the obj/Debug/net6.0/Protos path.  The file CountryGrpcGrpc.cs contains the class that we will use as a client to interact with the gRPC server.

5. Implement the Business Logic

Open an IDE to edit the Program.cs file and add the code to call the server:

C#
 
using System.Threading.Tasks;
using Grpc.Net.Client;
using Sumaris.Grpc.Services;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;


var countryNames = new string[] { "Perú", "Ecuador", "Chile", "Brasil", "Argentina", "Venezuela" };

// See https://aka.ms/new-console-template for more information
using var channel =
    GrpcChannel.ForAddress("http://localhost:5000");
CountryService.CountryServiceClient? client =
    new CountryService.CountryServiceClient(channel);

// Consume unitary
// rpc listAllCountries(google.protobuf.Empty) returns ( CountryList);
var reply = await client.listAllCountriesAsync(new Empty());
Console.WriteLine("Hello, World!");
Console.WriteLine("Greeting: " + reply.Country);


// Consume stream server
//rpc getAllCountries(google.protobuf.Empty) returns (stream Country);
printServerStream();


// Consume unitary
//rpc findCountryByName(FindCountryByNameRequest ) returns(FindCountryByNameResponse);


// Consume client streaming server streaming
//rpc findCountriesByNames( stream FindCountryByNameRequest)  returns (stream Country);
var asyncDuplexStreamingCall = client.findCountriesByNames();
callStreamFromClient(asyncDuplexStreamingCall.RequestStream);
getStreamFromServer(asyncDuplexStreamingCall.ResponseStream);


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



async void printServerStream()
{
    var reply2 = client.getAllCountries(new Empty()).ResponseStream;
    Console.WriteLine("Greeting2: ");
    while (await reply2.MoveNext())
    {
        Console.Write(reply2.Current);
    }
    Console.WriteLine();
}

async void callStreamFromClient(IClientStreamWriter<FindCountryByNameRequest> request)
{
    foreach (var countryName in countryNames)
    {
        var d = new FindCountryByNameRequest(); 
        d.Name = countryName;
        await request.WriteAsync(d);
    }
    await request.CompleteAsync();
}

async void getStreamFromServer(IAsyncStreamReader<Country> response)
{
    await foreach(var p in response.ReadAllAsync())
    {
        Console.Write($">>{response.Current}");
    }

    Console.WriteLine("Terminando ...");
}

In the next images, I want to explain a little more about how you consume the remote procedure. 

The step marked as 1 lets you create the "client" object. You need only one client to interact with the server.

From this point, you use the "client" to call each one of the remote procedures as you can see in step 2. In this case, the client is calling a server streaming RPC called getAllCountries. The server sends the data to the client asynchronously and in streaming fashion (step 3). The client reads the data in streaming fashion too until the server finishes the sending (step 4).


Now that we saw how you can call a streaming server gRPC, I will show you how you can call a bidirectional streaming gRPC.

In this case, we use the same "client" object created previously, step 1, and call the remote bidirectional streaming gRPC. In this example, it is findCountriesByName, which returns an object AsyncServerStreamingCall (step 2). This object wraps two objects: the RequestStream that is used to send streaming data to the server (step 3), and the ResponseStream that is used to get the streaming data returned by the server (step 5). 

The server processes each incoming object sent by the client in the transmission stream, applies its business logic, and asynchronously writes its response to the transmission return stream as we can see in step 4. The client reads the incoming responses sent by the server using the IAsyncStreamReader, as you can see in step 5.

When the client has no more data to send to the server, it must notify the server (red box in step 3), so that the server finishes its process of reading the request asynchronously and can leave the foreach in step 4. In this case, the server ends its process and notifies the client that there is no more data to read. At this point, the client exits the foreach in step 5.

This completes bidirectional transmission.

Now that I have shown you a bidirectional streaming gRPC, you can use it to implement a client streaming gRPC.

Conclusion

We  use the Protocol buffer files to generate the client implementation in .NET and the async/await to manage streaming server or client.  We saw how we can call a streaming server and bidirectional streaming gRPC.

Feel free to let me know if you have any questions or feedback. 

Thank you!

gRPC Net (command)

Opinions expressed by DZone contributors are their own.

Related

  • Playing with gRPC and .NET 6
  • Implementing API Design First in .NET for Efficient Development, Testing, and CI/CD
  • Docs That Write Themselves: Scaling With gRPC and Protobuf
  • Database Query Service With OpenAI and PostgreSQL in .NET

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!