Creating your first gRPC .NET Core Client and Server App using Visual Studio or Visual Studio Code
This is a quick setup guide for creating and running .NET core apps that use the gRPC framework for communication. This guide includes the setup for Visual Studio 2017 or above(Windows / MacOS) as well as Visual Studio Code (Windows / MacOS / Linux).
This tutorial includes the following:
- Creating your first protocol buffers
- IDE Environment Setup (Visual Studio & VS Code)
- Generating C# code from protocol buffers
- Creating the Server
- Creating the Client
In this tutorial, we will be creating a simple Account management system that will retrieve an employee's first and last name based on the employee's ID.
Prerequisites:
- Introductory knowledge of gRPC
- Introductory knowledge of .NET Core
- Visual Studio 2017 and above for .NET Core / VSCode
Step 1 : Creating your first protocol buffers
A protocol buffer defines the request and response parameters for client-server interaction. In this case, a service (Account Service) will be defined that would facilitate data exchange between the client and server by defining the request and response parameters.
"Protocol buffers are easy to read, flexible contracts that define client - server interaction via gRPC."
syntax = "proto3";
service AccountService {
rpc GetEmployeeName (EmployeeNameRequest) returns (EmployeeName);
}
message EmployeeNameRequest {
string emp_id = 1;
}
message EmployeeName {
string first_name = 1;
string last_name = 2;
}
The code snippet above contains the following:
- A service (AccountService) that serves as the entry point and has all the available rpc calls.
- An rpc (GetEmployeeName) which accepts a request message (EmployeeNameRequest) and returns a response message (EmployeeName).
- messages (EmployeeNameRequest and EmployeeName) that contain various data fields which are used for data format definition (similar to properties in classes).
More information about protocol buffers can be found here.
Save the following as "Accounts.proto" within a folder named protos. This folder will later be moved to the project, once the project is setup.
Step 2 : IDE Environment Setup (Visual Studio & VS Code)
The environment setup includes the following:
- Basic IDE Setup
- Setting up .NET Core
- Installing gRPC dependencies
Basic IDE Setup
For VS Code ensure that C# extension for Visual Studio Code has been installed.The extension can be found in the VS Code marketplace within the IDE or can be downloaded from here.
Setting up .NET Core
.NET core can be downloaded for all platforms here. Note that VS 2017 would already have this installed.
Installing gRPC dependencies(VS Code)
For VS Code, run the following commands to set up the project and it's dependencies. Create a new Project/Folder name: Accounts. This will be the gRPC server.
dotnet new console
dotnet add package Grpc
dotnet add package Grpc.Core
dotnet add package Grpc.Tools
dotnet add package Google.Protobuf
dotnet restore
From the snippet above:
- "dotnet new console": Creates the console app.
- "dotnet add package Grpc , Grpc.Core and Google.Protobuf": Installs the necessary gRPC dependencies
- "dotnet add package Grpc.Tools": Downloads the tools required to generate C# code from the protocol buffers.
- "dotnet restore": Restores necessary packages.
Installing gRPC dependencies (VS2017 and above)
In Visual studio, create a new .NET Core console project named Accounts. Add the following dependencies using the NuGet package manager: (Grpc , Grpc.Core, Google.Protobuf and Grpc.Tools)
These packages can be added by accessing Tools->NuGet Package Manager -> Manage NuGet Packages for Solution...
Step 3 : Generating C# code from protocol buffers
The C# code is generated from the protocol buffers using the protoc executable that is bundled along with the Grpc.Tools package.
Before generating the code, ensure that the protos folder that contains the protocol buffer created earlier is placed within the project directory structure. Create 2 other folders in the project : controller and modules. This will be re-visited later.
Your project structure should now look similar to the image(Left: Visual Studio , Right: VS Code).
If Grpc.Tools is installed correctly, the executable can be found in the following locations, based on the type of Operating System:
Mac (~/.nuget/packages) Windows (%UserProfile%\.nuget\packages) Linux (~/.nuget/packages)
Using the terminal / powershell, navigate to the project folder(Accounts) and run the command mentioned below. In the command which is specifically for the Windows OS, "C:\Users\Nikhil\" is the %UserProfile% variable, and needs to be altered accordingly based on the type of operating system and the username. Make sure that the changes are done for both the protoc and plugin before running the command.
C:\Users\Nikhil\.nuget\packages\grpc.tools\1.19.0\tools\windows_x64\protoc.exe --proto_path=protos --grpc_out=protos --csharp_out=protos --csharp_opt=file_extension=.g.cs Accounts.proto --plugin=protoc-gen-grpc=C:\Users\Nikhil\.nuget\packages\grpc.tools\1.19.0\tools\windows_x64\grpc_csharp_plugin.exe
The protoc command includes the following:
- --proto_path: Sets the path where the protocol buffer resides(in this case ,protos)
- --grpc_out: Sets the path for gRPC file output.
- --csharp_out: Sets the path for C# file output.
- --csharp_opt: Extra options.In this case,it sets the C# output extension to g.cs.
- Accounts.proto :Specifies the protocol buffer name.
- --plugin: The plugin to be used, in this case the grpc_csharp_plugin.
If successful, no message will be displayed. In the protos folder, two files will be generated(Accounts.g.cs and AccountsGrpc.cs). The generated C# files contains the classes and attributes necessary to implement the contract.
4. Creating the Server
Before creating the server, create a sample C# response class EmployeeData.cs with basic values in the modules folder as shown below:
using System;
namespace Accounts
{
public class EmployeeData
{
public EmployeeName GetEmployeeName(EmployeeNameRequest request)
{
EmployeeName empName = new EmployeeName();
switch(request.EmpId)
{
case "1":
empName.FirstName="John";
empName.LastName="Doe";
break;
case "2":
empName.FirstName="Dave";
empName.LastName="Williams";
break;
default:
break;
}
return empName;
}
}
}
EmployeeName and EmployeeNameRequest will be automatically referenced from the generated C# files. The method GetEmployeeName() contains the business logic. If emp ID is passed as 1, the response will have first name as "John" and last name as "Doe".
Create a class Controller.cs in the controller folder. Add the following code snippet:
using System;
using System.Threading.Tasks;
using Grpc.Core;
namespace Accounts
{
class AccountsImpl: AccountService.AccountServiceBase
{
// Server side handler of the GetEmployeeName RPCpublic override Task<EmployeeName> GetEmployeeName(EmployeeNameRequest request, ServerCallContext context)
{
EmployeeData empData = new EmployeeData();
return Task.FromResult( empData.GetEmployeeName(request) );
}
}
}
This class implements the AccountServiceBase class, and will be bound to the server. When the server receives a request for GetEmployeeName, it will pass the data to the previously created class EmployeeData, and return the appropriate response back to the caller.
Finally, set up the server bindings to the controller. Add the following code snippet to the entry point of the project(In this case, Program.cs)
using System;
using Grpc.Core;
namespace Accounts
{
class Program
{
const int Port = 50051;
public static void Main(string[] args)
{
try
{
Server server = new Server
{
Services = { AccountService.BindService(new AccountsImpl()) },
Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
};
server.Start();
Console.WriteLine("Accounts server listening on port " + Port);
Console.WriteLine("Press any key to stop the server...");
Console.ReadKey();
server.ShutdownAsync().Wait();
}
catch(Exception ex)
{
Console.WriteLine($"Exception encountered: {ex}");
}
}
}
}
Services = { AccountService.BindService(new AccountsImpl()) } binds the controller to the server, which is internally referencing the business logic that was setup earlier. Run the project to start the server. For VS Code use the command "dotnet run".
If the console output is similar to the image then the server is now up and running.
5. Creating the Client
Set up another project named Client and follow the same steps as mentioned in Step 2 to install all necessary packages related to gRPC(Grpc.Tools not required). The protos folder along with the files need not be regenerated, directly copy the protos folder to the new project.
Your project structure should now look similar to the image(Left: Visual Studio , Right: VS Code).
Add the following code snippet to Program.cs:
using Grpc.Core;
using System;
namespace Client
{
class Program
{
static void Main(string[] args)
{
try
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
var client = new AccountService.AccountServiceClient(channel);
EmployeeName empName = client.GetEmployeeName(new EmployeeNameRequest { EmpId = "1" });
if (empName == null || string.IsNullOrWhiteSpace(empName.FirstName) || string.IsNullOrWhiteSpace(empName.LastName))
{
Console.WriteLine("Employee not found.");
}
else
{
Console.WriteLine($"The employee name is {empName.FirstName} {empName.LastName}.");
}
channel.ShutdownAsync().Wait();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
catch(Exception ex)
{
Console.WriteLine($"Exception encountered: {ex}");
}
}
}
}
The client creates a channel and passes the request object. In this case, EmpId is passed as 1. Ensure that the server is running before the client app is executed. The expected console output is as follows:
Conclusion
gRPC has multiple benefits when it comes to cross service communications, as compared to conventional methods. A quick way to start learning is by referring to the well composed official documentation. The source code for both the projects are available on GitHub.
Senior Software Engineer bei Chromsystems Instruments & Chemicals GmbH
3 年Nice explanation...but consider https://github.com/grpc/grpc-dotnet because your native Grpc-Dependencies will be deprecated from Mai 2022 >> https://grpc.io/blog/grpc-csharp-future/ BindService-Method is not available anymore in this implementation...gRPC-Server should run as recommended AspDotNet-Process...