In this article, we will discuss gRPC and perform CRUD Operation using that and step-by-step implementation of gRPC.
We take Product Application here to understand how things are going to work with gRPC and, in that first, we create ProductOfferGrpcService which is used to create Product Offers and which will be consumed by Admin Service and he will add, update and delete product offer and managed all the things related to that using gRPC Service. Secondly, we create another section in ProductOfferGrpcService for the User to get a list of offers which is added by the admin.
Agenda
- Introduction of gRPC
- Implementation of ProductOfferGrpcService and Admin.API Microservice
- Implementation of User Console Application
Prerequisites
- Visual Studio 2022
- .NET Core 6 SDK
- SQL Server
Introduction of gRPC
- gRPC stands for Google Remote Procedure Calls
- gRPC is a modern open-source high-performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking, and authentication. It is also applicable in the last mile of distributed computing to connect devices, mobile applications, and browsers to backend services. – gRPC Page
- gRPC is the framework that is used to implement APIs using HTTP/2
- Basically, gRPC uses the protobuf for serialization and HTTP2 protocol which provides lots more advantages than HTTP
- gRPC clients and servers intercommunicate utilizing a variety of environments and machines, It Also supports many languages like Java, C#, Go, Ruby and Python.
- The Binary layer of gRPC will do all data operations like encoding and it also uses protobuf as an intermediator between client and server, improving performance.
- It is also used for communication between multiple microservices efficiently
If you want some more details about gRPC and how it will work then I suggest you read my following article
Implementation of ProductOfferGrpcService Service
Step 1
Create a new Blank Solution
Step 2
Configure Project
Step 3
Add a new gRPC Service Project inside the Blank Solution
Step 4
Configure your new project
Step 5
Provide additional information
Remove default protobuf and service file from the project
Project Structure
Step 6
Create Offer Class inside the Entities
namespace ProductOfferGrpcService.Entities
{
public class Offer
{
public int Id { get; set; }
public string ProductName { get; set; }
public string OfferDescription { get; set; }
}
}
Step 7
Next, Create a new DbContextClass inside the Data folder
using Microsoft.EntityFrameworkCore;
using ProductOfferGrpcService.Entities;
namespace ProductOfferGrpcService.Data
{
public class DbContextClass : DbContext
{
protected readonly IConfiguration Configuration;
public DbContextClass(IConfiguration configuration)
{
Configuration = configuration;
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
}
public DbSet<Offer> Offer { get; set; }
}
}
Step 8
Later on, create IProductOfferService and ProductOfferService inside the Repositories folder
IProductOfferService
using ProductOfferGrpcService.Entities;
namespace ProductOfferGrpcService.Repositories
{
public interface IProductOfferService
{
public Task<List<Offer>> GetOfferListAsync();
public Task<Offer> GetOfferByIdAsync(int Id);
public Task<Offer> AddOfferAsync(Offer offer);
public Task<Offer> UpdateOfferAsync(Offer offer);
public Task<bool> DeleteOfferAsync(int Id);
}
}
ProductOfferService
using Microsoft.EntityFrameworkCore;
using ProductOfferGrpcService.Data;
using ProductOfferGrpcService.Entities;
namespace ProductOfferGrpcService.Repositories
{
public class ProductOfferService : IProductOfferService
{
private readonly DbContextClass _dbContext;
public ProductOfferService(DbContextClass dbContext)
{
_dbContext = dbContext;
}
public async Task<List<Offer>> GetOfferListAsync()
{
return await _dbContext.Offer.ToListAsync();
}
public async Task<Offer> GetOfferByIdAsync(int Id)
{
return await _dbContext.Offer.Where(x => x.Id == Id).FirstOrDefaultAsync();
}
public async Task<Offer> AddOfferAsync(Offer offer)
{
var result = _dbContext.Offer.Add(offer);
await _dbContext.SaveChangesAsync();
return result.Entity;
}
public async Task<Offer> UpdateOfferAsync(Offer offer)
{
var result = _dbContext.Offer.Update(offer);
await _dbContext.SaveChangesAsync();
return result.Entity;
}
public async Task<bool> DeleteOfferAsync(int Id)
{
var filteredData = _dbContext.Offer.Where(x => x.Id == Id).FirstOrDefault();
var result = _dbContext.Remove(filteredData);
await _dbContext.SaveChangesAsync();
return result != null ? true : false;
}
}
}
Step 9
Create OfferMapper inside AutoMapper folder
using AutoMapper;
using ProductOfferGrpcService.Entities;
using ProductOfferGrpcService.Protos;
namespace ProductOfferGrpcService.AutoMapper
{
public class OfferMapper : Profile
{
public OfferMapper()
{
CreateMap<Offer, OfferDetail>().ReverseMap();
}
}
}
Step 10
Next, create a new offer proto file inside Proto
syntax = "proto3";
option csharp_namespace = "ProductOfferGrpcService.Protos";
service ProductOfferService {
rpc GetOfferList (Empty) returns (Offers);
rpc GetOffer (GetOfferDetailRequest) returns (OfferDetail);
rpc CreateOffer (CreateOfferDetailRequest) returns (OfferDetail);
rpc UpdateOffer (UpdateOfferDetailRequest) returns (OfferDetail);
rpc DeleteOffer (DeleteOfferDetailRequest) returns (DeleteOfferDetailResponse);
}
message GetOfferDetailRequest {
int32 productId = 1;
}
message OfferDetail {
int32 id = 1;
string productName = 2;
string offerDescription = 3;
}
message CreateOfferDetailRequest {
OfferDetail offer = 1;
}
message UpdateOfferDetailRequest {
OfferDetail offer = 1;
}
message DeleteOfferDetailRequest {
int32 productId = 1;
}
message DeleteOfferDetailResponse {
bool isDelete = 1;
}
message Empty{
}
message Offers {
repeated OfferDetail items = 1;
}
Also, make sure proto file properties are correct as I showed below and if that will be correct then build your project
Step 11
Add a new Offer Service inside Services
using AutoMapper;
using Grpc.Core;
using ProductOfferGrpcService.Entities;
using ProductOfferGrpcService.Protos;
using ProductOfferGrpcService.Repositories;
using ProductOfferService = ProductOfferGrpcService.Protos.ProductOfferService;
namespace ProductOfferGrpcService.Services
{
public class OfferService : ProductOfferService.ProductOfferServiceBase
{
private readonly IProductOfferService _prductOfferService;
private readonly IMapper _mapper;
public OfferService(IProductOfferService prductOfferService, IMapper mapper)
{
_prductOfferService = prductOfferService;
_mapper = mapper;
}
public async override Task<Offers> GetOfferList(Empty request, ServerCallContext context)
{
var offersData = await _prductOfferService.GetOfferListAsync();
Offers response = new Offers();
foreach (Offer offer in offersData)
{
response.Items.Add(_mapper.Map<OfferDetail>(offer));
}
return response;
}
public async override Task<OfferDetail> GetOffer(GetOfferDetailRequest request, ServerCallContext context)
{
var offer = await _prductOfferService.GetOfferByIdAsync(request.ProductId);
var offerDetail = _mapper.Map<OfferDetail>(offer);
return offerDetail;
}
public async override Task<OfferDetail> CreateOffer(CreateOfferDetailRequest request, ServerCallContext context)
{
var offer = _mapper.Map<Offer>(request.Offer);
await _prductOfferService.AddOfferAsync(offer);
var offerDetail = _mapper.Map<OfferDetail>(offer);
return offerDetail;
}
public async override Task<OfferDetail> UpdateOffer(UpdateOfferDetailRequest request, ServerCallContext context)
{
var offer = _mapper.Map<Offer>(request.Offer);
await _prductOfferService.UpdateOfferAsync(offer);
var offerDetail = _mapper.Map<OfferDetail>(offer);
return offerDetail;
}
public async override Task<DeleteOfferDetailResponse> DeleteOffer(DeleteOfferDetailRequest request, ServerCallContext context)
{
var isDeleted = await _prductOfferService.DeleteOfferAsync(request.ProductId);
var response = new DeleteOfferDetailResponse
{
IsDelete = isDeleted
};
return response;
}
}
}
Step 12
Configure the database connection string inside the app settings file
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=DESKTOP;;Initial Catalog=ProductOfferAsync;User Id=sa;Password=database;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http2"
}
}
}
Step 13
Register and configure a few services inside the Program class
using ProductOfferGrpcService.Data;
using ProductOfferGrpcService.Repositories;
using ProductOfferGrpcService.Services;
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.Services.AddScoped<IProductOfferService, ProductOfferService>();
builder.Services.AddDbContext<DbContextClass>();
builder.Services
.AddAutoMapper(typeof(Program).Assembly);
var app = builder.Build();
// Configure the HTTP request pipeline.
app.MapGrpcService<OfferService>();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
app.Run();
Implementation of Admin.API Microservice
Step 1
Create a new Admin.API Web API Project
Project Structure
Step 2
Connect the ProductOfferGrpcService
Right-Click on Admin.API and click on Connected Service in Add section
Step 3
Add a new gRPC Service
Step 4
Click on gRPC
Step 5
Provide the protobuf file path and make sure your type of class is also correct
Step 6
Click on finish it will configure all things
Step 7
Create the Offer Class inside Entities
namespace Admin.API.Entities
{
public class Offer
{
public int Id { get; set; }
public string ProductName { get; set; }
public string OfferDescription { get; set; }
}
}
Step 8
Next, add ProductOfferController
using Admin.API.Entities;
using Grpc.Net.Client;
using Microsoft.AspNetCore.Mvc;
using ProductOfferGrpcService.Protos;
namespace Admin.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductOfferController : ControllerBase
{
private readonly GrpcChannel _channel;
private readonly ProductOfferService.ProductOfferServiceClient _client;
private readonly IConfiguration _configuration;
public ProductOfferController(IConfiguration configuration)
{
_configuration = configuration;
_channel =
GrpcChannel.ForAddress(_configuration.GetValue<string>("GrpcSettings:OfferServiceUrl"));
_client = new ProductOfferService.ProductOfferServiceClient(_channel);
}
[HttpGet("getofferlist")]
public async Task<Offers> GetOfferListAsync()
{
try
{
var response = await _client.GetOfferListAsync(new Empty { });
return response;
}
catch
{
}
return null;
}
[HttpGet("getofferbyid")]
public async Task<OfferDetail> GetOfferByIdAsync(int Id)
{
try
{
var request = new GetOfferDetailRequest
{
ProductId = Id
};
var response = await _client.GetOfferAsync(request);
return response;
}
catch
{
}
return null;
}
[HttpPost("addoffer")]
public async Task<OfferDetail> AddOfferAsync(Offer offer)
{
try
{
var offerDetail = new OfferDetail
{
Id = offer.Id,
ProductName = offer.ProductName,
OfferDescription = offer.OfferDescription
};
var response = await _client.CreateOfferAsync(new CreateOfferDetailRequest()
{
Offer = offerDetail
});
return response;
}
catch
{
}
return null;
}
[HttpPut("updateoffer")]
public async Task<OfferDetail> UpdateOfferAsync(Offer offer)
{
try
{
var offerDetail = new OfferDetail
{
Id = offer.Id,
ProductName = offer.ProductName,
OfferDescription = offer.OfferDescription
};
var response = await _client.UpdateOfferAsync(new UpdateOfferDetailRequest()
{
Offer = offerDetail
});
return response;
}
catch
{
}
return null;
}
[HttpDelete("deleteoffer")]
public async Task<DeleteOfferDetailResponse> DeleteOfferAsync(int Id)
{
try
{
var response = await _client.DeleteOfferAsync(new DeleteOfferDetailRequest()
{
ProductId = Id
});
return response;
}
catch
{
}
return null;
}
}
}
Step 9
Configure the gRPC Service URL inside the app settings file
{
"GrpcSettings": {
"OfferServiceUrl": "http://localhost:5263"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Step 10
Right-Click on the solution and set both projects as a startup project
Step 11
Finally, run your project
Here you can use endpoints to manage offers through gRPC Service
Implementation of User Console Application
We are going to create a console application to get a list of offers that which admin adds through gRPC
Step 1
First, we create a new proto file inside ProductOfferGrpcService named as user offer proto file
syntax = "proto3";
option csharp_namespace = "ProductOfferGrpcService.Protos";
service UserOfferService {
rpc GetUserOfferList (EmptyRequestArg) returns (UserOffers);
}
message UserOfferDetail {
int32 id = 1;
string productName = 2;
string offerDescription = 3;
}
message EmptyRequestArg{
}
message UserOffers {
repeated UserOfferDetail items = 1;
}
Step 2
Create UserOfferMapper inside AutoMapper of ProductOfferGrpcService
using AutoMapper;
using ProductOfferGrpcService.Entities;
using ProductOfferGrpcService.Protos;
namespace ProductOfferGrpcService.AutoMapper
{
public class UserOfferMapper : Profile
{
public UserOfferMapper()
{
CreateMap<Offer, UserOfferDetail>().ReverseMap();
}
}
}
Step 3
Next, add a new UserOfferService inside the Services of ProductOfferGrpcService
using AutoMapper;
using Grpc.Core;
using ProductOfferGrpcService.Protos;
using ProductOfferGrpcService.Repositories;
using ProductOfferService = ProductOfferGrpcService.Protos.UserOfferService;
namespace ProductOfferGrpcService.Services
{
public class UsersOfferService : ProductOfferService.UserOfferServiceBase
{
private readonly IProductOfferService _prductOfferService;
private readonly IMapper _mapper;
public UsersOfferService(IProductOfferService prductOfferService, IMapper mapper)
{
_prductOfferService = prductOfferService;
_mapper = mapper;
}
public async override Task<UserOffers> GetUserOfferList(EmptyRequestArg request, ServerCallContext context)
{
var offersData = await _prductOfferService.GetOfferListAsync();
UserOffers response = new UserOffers();
foreach (var offer in offersData)
{
response.Items.Add(_mapper.Map<UserOfferDetail>(offer));
}
return response;
}
}
}
Step 4
Configure UserOfferService inside the Program class of ProductOfferGrpcService
using ProductOfferGrpcService.Data;
using ProductOfferGrpcService.Repositories;
using ProductOfferGrpcService.Services;
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.Services.AddScoped<IProductOfferService, ProductOfferService>();
builder.Services.AddDbContext<DbContextClass>();
builder.Services
.AddAutoMapper(typeof(Program).Assembly);
var app = builder.Build();
// Configure the HTTP request pipeline.
app.MapGrpcService<OfferService>();
app.MapGrpcService<UsersOfferService>();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
app.Run();
Step 5
Create a new User console application
Step 6
Configure your project
Step 7
Add a user offer proto file inside the User Application
Step 8
Add a new reference service
Step 9
Click on gRPC
Step 10
Select file URL and type of class properly
Step 11
Add code inside Program class which connects gRPC Service and takes a list of offers which is added by Admin
using Grpc.Net.Client;
using ProductOfferGrpcService.Protos;
var channel = GrpcChannel.ForAddress("http://localhost:5263");
var client = new UserOfferService.UserOfferServiceClient(channel);
var serverReply = client.GetUserOfferList(new EmptyRequestArg { });
Console.WriteLine(serverReply);
Console.ReadLine();
Step 12
Finally, whenever you run your console application will see the latest offer added by the admin
Output
Conclusion
Here we discussed gRPC and the step-by-step implementation of gRPC Product Service. Also, implementation of Admin service and User Application.
Happy Learning!