gRPC in ASP.Net Core

gRPC is an open-source, high-performance RPC framework. In this blog, I am going to cover gRPC in ASP.Net Core. We can use gRPC within or across data centers. We can use gRPC in mobile applications, web applications as well as edge computing as a backend service.

Google was the first to develop gRPC, later it open-sourced it. gRPC uses HTTP 2 for the transport protocol. It uses protobuf (or protocol buffer) for defining interfaces. gRPC is a contract first framework, allowing us to build contract first based distributed systems.

When it comes to the analogy, gRPC is more close to Remoting than compared to REST Services. In gRPC client can make a server method call, as if the method is in the local object. This makes it really easy to create distributed systems. Whereas REST services are based on resource access.

We can build gRPC clients and servers in completely different programming languages. Meaning, the server can be in C#, whereas the client can be in Python.

By default gRPC uses protobuf for serializing data, however, it can be using JSON format as well.

gRPC was introduced in .Net Core with the release of .Net Core 3.0. Hence you will need .Net Core 3.0 installed for gRPC to work.

gRPC in ASP.Net Core

The five main features of gRPC

Firstly, it supports a simple service definition. We define The services using protobuf (or protocol buffer). Protobuf (or protocol buffer) is a language-agnostic, platform-agnostic mechanism for serializing data (Similar to JSON or XML). It is extremely faster compared to the XML or JSON serializer.

Secondly, very quick to start and scales. Setting up a development environment and starting to code is extremely simple and fast. Also, the framework can handle millions of gRPC calls per second.

Thirdly, gRPC works across languages and platforms. The framework creates autogenerated classes across languages. Wich makes it very easy to get started.

Fourthly, it supports bi-directional streaming across services.

Finally, authentication is fully integrated with the gRPC framework.

Four types of remote procedure call in gRPC

Firstly unary remote procedure call. In this, the client sends a single request to the server. And gets a single response back from the server.

Secondly, the server streaming remote procedure calls. In this case, the clients sends a single request to the server. And gets a stream of data as a message back from the server.

Thirdly, the client streaming remote procedure calls. In this case, the client sends the request as a stream of data to the server. And server waits for the complete stream, and finally returns response to the client.

Fourthly, bi-directional streaming remote procedure calls. In this case, clients sends a stream of request to the server. And on the other side the server sends a stream of response back to the client. The server might choose to wait for the entire request stream, or can act on one message at a time. This depends on the business implementation.

Creating gRPC in ASP.Net Core

For creating a gRPC server in ASP.Net Core, firstly, I am going to create a new ASP.Net Core Web application in Visual Studio 2019 (You can use other versions of Visual Studio as well. As long as it supports .Net Core 3.0).

I will select “Empty” project template. And I will keep the “Configure for HTTPS” checkbox on in the Advanced settings section.

gRPC in ASP.Net Core

Once the project is created, I am going to create a new folder inside the project and name it as Protos. I will keep all the gRPC contract files inside of this folder. The folder name Protos is a standard everyone uses, but it can have any name. Also, the contract files can be anywhere in the project as long as we configure those properly. And I am going to discuss the configuration soon.

Installing necessary NuGet packages

For gRPC in ASP.Net Core to work, we will install all the necessary NuGet packages. We will install the following packages:

  • Grpc.Tools
  • Grpc.Core
  • Grpc.AspNetCore

The Grpc.Tools is the package which will create the autogenerated code from .proto files. It reads the ItemGroup configuration from the project file for identifying the gRPC service definitions.

Creating .proto file for gRPC contract

As I mentioned above, protobuf is used as the language for the gRPC service contracts. And the extension for protobuf file is .proto. Hence I will create the contract for my project as well in a .proto extension file.

I will build a service that will return the population of a state based on the name of the state. Hence I will create a population.proto file inside of the Protos folder.

Once the file is created I will add the following code inside the .proto file.

syntax = "proto3";

option csharp_namespace = "GrpcPopulation";

service PopulationProvider {
	rpc GetPopulation (PopulationRequest) returns (PopulationResponse);
}

message PopulationRequest{
	string State = 1;
}

message PopulationResponse{
	string Count = 1;
}

Let us go through each of the line items in the above file.

The first line defines the version of the protobuf we are going to use. For gRPC the recommendation is to use version 3 or proto3.

The second line defines the namespace of the autogenerated class created by the gRPC framework. This line is not necessary but helps if we want to isolate the autogenerated class from the rest of the code.

The third line defines the remote procedure. Inside the service definition, we will put the RPC method that we want to expose. The syntax is straight forward, it starts with rpc, followed by the method name, then the request type then returns and finally the response type.

The next two line defines the request and response type. The number after the property is the position of the property. For example, in the PopulationRequest type, the string State = 1, means the position of the State property is first.

Update Server Project file

Once we finish defining the population.proto file, it is time to update the project file. We will add a new ItemGroup section in the project file for configuring .proto files.

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

In the above ItemGroup section, Include attribute defines the location of the gRPC service definition protobuf files. And the GrpcServices attribute defines if it is a server or client application. The GrpcServices attribute can have either Server or Client as the possible values.

Once the server project is configured, I will build the application. This will create the autogenerated gRPC files necessary for the server code.

Creating gRPC service

Next, I will create a new class PopulationService. This class will contain the gRPC method for clients to call. This class will derive from PopulationProvider.PopulationProviderBase class, which is the autogenerated class from the population.proto file.

Inside of this class, I will override the GetPopulation method. This is the method I defined inside of the population.proto file is the remote procedure.

The implementation of the GetPopulation will be simple. For the time being, I will return a hardcoded value of 1000 for the population count irrespective of the input request state.

using Grpc.Core;
using GrpcPopulation;
using System.Threading.Tasks;

namespace GrpcDemo.Server
{
    public class PopulationService : PopulationProvider.PopulationProviderBase
    {
        public override Task<PopulationResponse> GetPopulation(
            PopulationRequest populationRequest, 
            ServerCallContext serverCallContext)
        {
            return Task.FromResult(new PopulationResponse { Count = "1000" });
        }
    }
}

Update Startup

Once the service class is created, it is time to configure gRPC in the ASP.Net core pipeline.

To Achieve this, firstly I will update the ConfigureServices method inside of the Startup class to add gRPC.

public void ConfigureServices(IServiceCollection services)
{
    services.AddGrpc();
}

Secondly, I will update the Configure method inside of the Startup class. Inside of the Configure method, I will map the gRPC endpoint.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });

    app.UseEndpoints(e => e.MapGrpcService<PopulationService>());
}

At this point the service is ready. I will start the service.

gRPC service console log

In the console log of the gRPC server, you can see the following line “Added gRPC method ‘GetPopulation’ to service ‘PopulationProvider’. Method type: ‘Unary’, route pattern: ‘/PopulationProvider/GetPopulation’“. This implies that we are running the first RPC type supported by gRPC. It is the unary RPC.

Creating Client for gRPC in ASP.Net Core

Now that the server is up and running, it is time to create a gRPC client. For the client, I will create a new .Net Core Console application using Visual Studio 2019.

Once the project is created, I will create a new folder named “Protos” inside of the project. As I mentioned before, gRPC is a contract based framework. Hence I will copy the same population.proto file which is defined in the server inside of the Protos folder.

Installing NuGet packages

Just as we did for the server, we will need to install gRPC related NuGet packages in the client as well. There are a couple of common packages, but the client also needs Grpc.Net.Client, which is unique to the client.

I will install the following NuGet packages:

  • Grpc.Tools
  • Grpc.Core
  • Grpc.Net.Client
  • Google.Protobuf

Update Client Project file

Once we finish installing the NuGet packages, it is time to update the project file. I will add a new ItemGroup section in the project file for configuring .proto files just as I did for the server.

The only difference is that for the GrpcServices attribute, instead of Server, here I will set Client for the value.

<ItemGroup>
    <Protobuf Include="Protos\population.proto" GrpcServices="Client" />
</ItemGroup>

Calling gRPC service

Once the client project is also configured, I will build the application. This will create the autogenerated gRPC files necessary for the client code.

Next, inside of the Main method, inside of the Program class, I will create the gRPC channel. I will use GrpcChannel.ForAddress to create a new gRPC channel. And I will pass the localhost address of the gRPC server to the ForAddress method.

var channel = GrpcChannel.ForAddress("https://localhost:5001/");

Once the channel is created, I will create a gRPC client. And for creating the client, I will create a new instance of PopulationProviderClient, which is an autogenerated class from the population.proto file.

var client = new GrpcPopulation.PopulationProvider.PopulationProviderClient(channel);

And you can notice in the above code, the namespace is GrpcPopulation. Which is what I provided in the population.proto file.

Finally, I will call the GetPopulation method on the server to get the population of New Jersey (tough in server irrespective of state it will always return 1000 as population).

var population = client.GetPopulation(new GrpcPopulation.PopulationRequest { State = "NJ" });

The complete client code is below.

using Grpc.Net.Client;
using System;

namespace GrpcDemo.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var channel = GrpcChannel.ForAddress("https://localhost:5001/");
            var client = new GrpcPopulation.PopulationProvider.PopulationProviderClient(channel);

            var population = client.GetPopulation(new GrpcPopulation.PopulationRequest { State = "NJ" });
            Console.WriteLine(population.Count);
        }
    }
}

Once I run the client code, I can see the response 1000 in the output.

gRPC .Net Core client output

gRPC and Dependency Injection

gRPC supports Dependency injection in ASP.Net core just like any other features. As long as we configure a type in the dependency injection container, it will be available in the gRPC service.

To demonstrate this, I will create a new interface called IStatePopulationProvider. This interface will have a single method Get.

namespace GrpcDemo.Server
{
    public interface IStatePopulationProvider
    {
        long Get(string state);
    }
}

Once the interface is defined, I will create an implementation class StatePopulationProvider. This class will keep an in-memory dictionary of the state name and its population.

using System.Collections.Generic;

namespace GrpcDemo.Server
{
    public class StatePopulationProvider : IStatePopulationProvider
    {
        private readonly IDictionary<string, long> states = new Dictionary<string, long> {
            { "NJ", 10000 },
            { "NY", 20000 },
            { "MD", 30000 },
            { "KY", 40000 },
        };

        public long Get(string state)
        {
            if (states.ContainsKey(state))
            {
                return states[state];
            }
            return 0;
        }
    }
}

After the StatePopulationProvider class is implemented, I will update PopulationService to use IStatePopulationProvider. Hence, instead of a constant population count, PopulationService will use IStatePopulationProvider instance to get population count by state name.

using Grpc.Core;
using GrpcPopulation;
using System.Threading.Tasks;

namespace GrpcDemo.Server
{
    public class PopulationService : PopulationProvider.PopulationProviderBase
    {
        private readonly IStatePopulationProvider statePopulationProvider;

        public PopulationService(IStatePopulationProvider statePopulationProvider)
        {
            this.statePopulationProvider = statePopulationProvider;
        }

        public override Task<PopulationResponse> GetPopulation(
            PopulationRequest populationRequest, 
            ServerCallContext serverCallContext)
        {
            var count = statePopulationProvider.Get(populationRequest.State);
            return Task.FromResult(new PopulationResponse { Count = count.ToString() });
        }
    }
}

Once this is complete, I will configure the IStatePopulationProvider and its implementation class StatePopulationProvider in the Startup class. I will update the ConfigureServices method to register StatePopulationProvider in the dependency injection container.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IStatePopulationProvider, StatePopulationProvider>();

    services.AddGrpc();
}

Once everything is complete I will start the server. And then I will run the client code to test everything up. And the result will be as expected.

Conclusion

Creating an application to run gRPC in ASP.Net Core is as simple as it can get. The NuGet packages provided by gRPC does all the heavy lifting. We just need to take care of creating the .proto files.

Github repo: https://github.com/choudhurynirjhar/gRPC

YouTube video: https://youtu.be/OhijduW7jBA

References:

https://docs.microsoft.com/en-us/aspnet/core/grpc/basics?view=aspnetcore-3.1