CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

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

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

  • 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

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 2

Configure Project

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 3

Add a new gRPC Service Project inside the Blank Solution

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 4

Configure your new project

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 5

Provide additional information

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Remove default protobuf and service file from the project

Project Structure

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

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

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

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

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 2

Connect the ProductOfferGrpcService

Right-Click on Admin.API and click on Connected Service in Add section

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 3

Add a new gRPC Service

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 4

Click on gRPC

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 5

Provide the protobuf file path and make sure your type of class is also correct

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 6

Click on finish it will configure all things

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

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

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 11

Finally, run your project

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

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

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 6

Configure your project

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 7

Add a user offer proto file inside the User Application

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 8

Add a new reference service

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 9

Click on gRPC

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Step 10

Select file URL and type of class properly

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

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

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

Output

CRUD Operation And Microservice Communication Using gRPC In .NET Core 6 Web API

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!