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

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Playing With gRPC and .NET 6: Client Side
  • Docs That Write Themselves: Scaling With gRPC and Protobuf
  • Database Query Service With OpenAI and PostgreSQL in .NET
  • Yes! I Can Finally Run My .NET Application on Heroku!

Trending

  • How to Build Scalable Mobile Apps With React Native: A Step-by-Step Guide
  • Accelerating AI Inference With TensorRT
  • Why Documentation Matters More Than You Think
  • Performance Optimization Techniques for Snowflake on AWS

Playing with gRPC and .NET 6

Create a gRPC server in .NET.

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

Join the DZone community and get the full member experience.

Join For Free

In this tutorial, I want to show you, step by step, how you can create a gRPC server in .NET that implements the interface defined in the "proto" file.

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 for 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 such as Postman and Swagger UI.
brew install grpcui

Steps

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

  1. Create a project and configure it to generate the server stubs.
  2. Create the proto file.
  3. Register the proto file on your project and compile the project.
  4. Implement the business logic.
  5. Register the classes on Program.cs.
  6. Set up Kestrel to enable HTTP/2 without TLS (on development environment).
  7. Start the server and test using a client (I will use grpcurl).
In this example, we will use a .NET project called CountryGrpcServer that implements the business logic to search, create, or get a list of countries with some basic data. The CountryGrpcServer.proto file (described below) declares the remote procedures.

1. Create a Project and Configure It to Generate the Server Stubs

To create a gRPC template .NET project, open a terminal and execute the next command:
 
dotnet new grpc -o grpc.country.server -n CountryGrpcServer

The output is something like this:

Here:

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

2. Create the Proto File

The proto file defines the messages (data structure) and the methods that will be exposed by the server and consume by the client. The next image is a simplified view of the proto file:

For this tutorial, I've create a CountryGrpcServer.proto file and save it in the Protos directory located in the project.

 
vim grpc.country.server/Protos/CountryGrpcServer.proto

Copy the next lines in the file:

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;}

3. Register the Proto File on Your Project and Compile the Project

To register the project, you can:

A. Edit the csproj file and add the next lines. 

XML
 
<ItemGroup>
    <Protobuf Include="Protos\CountryGrpcServer.proto" GrpcServices="Server">
      <Link>Protos\CountryGrpcServer.proto</Link>
      <Access>Public</Access>
      <ProtoCompile>True</ProtoCompile>
      <CompileOutputs>True</CompileOutputs>
      <OutputDir>obj\Debug\net6.0\</OutputDir>
      <Generator>MSBuild:Compile</Generator>
    </Protobuf>
</ItemGroup>


B. Or, using the Visual Studio Code, follow the next steps:

i. Right-click on Connected Services -> Open service gallery.

ii. Select the option "Create a new service reference of an API for gRPC."

iii. Select the "add" button.

iv. Select the CountryGrpcServer.proto file created on the Protos directory of our project and select the class type as Server.


Now, please, open a terminal, move into the project created in step one, and compile the project.

 
dotnet build

 The output looks something like this:

When you build the project, dotnet creates two classes on the $ProjectPath/obj/Debug/net6.0/Protos/ directory. These classes are:

Both classes are in the Sumaris.Grpc.Services namespace. This is the value of the option csharp_namespace declared on the proto file.

The CountryGrpcServerGrpc.cs defines the partial class CountryService (this is the name of the service declared on the proto file) which contains an abstract inner class called CountryServiceBase (the name is created using the name of the service plus the word Base).

This abstract class declares the same methods defined on the proto file and will be the base class that we will inherit from to implement our business class.

4. Implement the Business Logic

Now, we will create a class that has our business logic. This class has to extend from the abstract base class Sumaris.Grpc.Services.CountryService.CountryServiceBase.

I've create the class BusinessCountryService on the file located in Services subdirectory with the next lines:

C#
 
using System;
using Sumaris.Grpc.Services;
using Google.Protobuf.WellKnownTypes;
using static Sumaris.Grpc.Services.CountryService;
using Grpc.Core;

namespace CountryGrpcServer.Services
{
    public class BusinessCountryService: CountryServiceBase
    {
        private static List<Country> countries;

        static BusinessCountryService()
        {
            countries = new List<Country>();
            countries.Add(new Country() { Area = 1285216.60f, CapitalCity = "Lima", Name = "Perú" });
            countries.Add(new Country() { Area = 756102.4f, CapitalCity = "Santiago de Chile", Name = "Chile" });
            countries.Add(new Country() { Area = 283561f, CapitalCity = "Quito", Name = "Ecuador" });
            countries.Add(new Country() { Area = 1141748f, CapitalCity = "Bogotá", Name = "Colombia" });
            countries.Add(new Country() { Area = 2791820f, CapitalCity = "Buenos Aires", Name = "Argentina" });
        }

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

        /** Server streamin RPC procedure
         * rpc getAllCountries(google.protobuf.Empty) returns(stream Country)
         * We use the async and await 
        */
        public override async Task getAllCountries(Empty request,IServerStreamWriter<Country> responseStream, ServerCallContext context) {
            foreach (var t in countries) { await responseStream.WriteAsync(t); }
        }

        /** Unitary RPC procedure
         *  rpc listAllCountries(google.protobuf.Empty) returns ( CountryList);
        */
        public override Task<CountryList> listAllCountries( Empty request,ServerCallContext context)
        {
            var r = new CountryList();
            r.Country.AddRange(countries);
            countries.ForEach(t => r.Country.Add(t));
            return Task.FromResult(r);
        }

        /** Unitary RPC procedure
         *  rpc findCountryByName( FindCountryByNameRequest ) returns (FindCountryByNameResponse);
         */
        public override Task<FindCountryByNameResponse> findCountryByName(FindCountryByNameRequest request,ServerCallContext context)
        {
            var r = new FindCountryByNameResponse();
            r.Country = countries.Find(c => c.Name.ToLower().Equals(request.Name.ToLower()));
            return Task.FromResult(r);
        }

        /** Unitary RPC procedure
         *  rpc createCountry (CountryCreateRequest) returns (CountryCreateRespopnse);
         */
        public override Task<CountryCreateRespopnse> createCountry(CountryCreateRequest request,ServerCallContext context)
        {
            countries.Add(request.Country);
            var r = new CountryCreateRespopnse();
            r.Created = true;
            return Task.FromResult(r);
        }

        /** Biderectional streaming RPC
         * rpc findCountriesByNames( stream FindCountryByNameRequest) returns (stream Country);
         */
        public override async Task findCountriesByNames(IAsyncStreamReader<FindCountryByNameRequest> requestStream,
            IServerStreamWriter<Country> responseStream,ServerCallContext context){
            await foreach (var t in requestStream.ReadAllAsync())
            {
                var r = countries.Find(c => c.Name.ToLower().Equals(t.Name.ToLower()));
                if (r is not null)
                {
                    await responseStream.WriteAsync(r);
                }
            }
        }
    }
}

Please note that if the server has to response in a streaming manner, the method has to be async and use the await word when you write asynchronously in the output channel. 

If the client send a stream request, you have to you use the await word to read asynchronously and the method have to be marked as async again.

5. Register the Classes on Program.cs

The next step is register our business class on the web application using the method MapGrpcService of the WebApplication instance.

Please open the Programs.cs file and copy the next:

C#
 
app.MapGrpcService<BusinessCountryService>();

6. Set Up Kestrel to Enable HTTP/2 Without TLS (on Development Environment)

Kestrel doesn’t support HTTP/2 with TLS on macOS systems, and we need to turn off TLS (you don't have to do this for the production environment, however, because you should use TLS).

 Please copy the next lines on the Program.cs:

C#
 
builder.WebHost.ConfigureKestrel(options =>
{
  // Setup a HTTP/2 endpoint without TLS.
  options.ListenLocalhost(5000, o => o.Protocols =
      HttpProtocols.Http2);
});

After all, the Program.cs file should be as follows:

C#
 
using CountryGrpcServer.Services;
using Microsoft.AspNetCore.Server.Kestrel.Core;

var builder = WebApplication.CreateBuilder(args);

// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682

// Add services to the container.
builder.Services.AddGrpc();
builder.WebHost.ConfigureKestrel(options =>
{
    // Setup a HTTP/2 endpoint without TLS.
    options.ListenLocalhost(5000, o => o.Protocols =
        HttpProtocols.Http2);
});

var app = builder.Build();

// Configure the HTTP request pipeline.
app.MapGrpcService<BusinessCountryService>();
app.Run();

7. Start the Server and Test Using a Client (I Will Use grpcurl)

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

 
dotnet run

Execute the next command in a terminal (Move into the project):

 
grpcurl -d  '{"name":"Peru"}{"name":"Ecuador"}{"name":"Chile"}' \
-proto ./Protos/CountryGrpcServer.proto \
-plaintext localhost:5000 \
CountryService/findCountriesByNames

Note that I'm using a relative path to the CountryGrpcServer.proto file.

Now, the server returns:

Conclusion

We used the Protocol buffer files to generate the server implementation in .NET and the async/await to manage streaming server or client. 

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

In my next article, I will show you how we can create a gRPC client using .NET. 

gRPC Net (command)

Opinions expressed by DZone contributors are their own.

Related

  • Playing With gRPC and .NET 6: Client Side
  • Docs That Write Themselves: Scaling With gRPC and Protobuf
  • Database Query Service With OpenAI and PostgreSQL in .NET
  • Yes! I Can Finally Run My .NET Application on Heroku!

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!