11

gRPC With Blazor, C# And .Net Core

 3 years ago
source link: https://dotnetgik.com/2020/01/21/grpc-with-blazor-c-and-net-core/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Agenda 

  1. Introduction
  2. Project setup
  3. Implementing gRPC Service 
  4. Implementing Blazor App
  5. Integrating the Blazor and gRPC Service
  6. Source Code 

Introduction

 gRPC has become the main talking point recently after the launch of the .Net core 3.0. This article will demonstrate one ToDo Blazor app which will be integrated to the gRPC service and perform the basic CRUD operation. If you are new to the gRPC and its terminologies just go through my previous articles.

Project setup

 To get the gRPC Service as well as Blazor server up and running we need basic installation of two things

  • .Net Core SDK 3.0 or Later
  • Visual studio 2019

Now there are two types of projects we are going to add here first is the gRPC Project and another will be the gRPC service project. Adding gRPC Service Go to Add New Project and select gRPC Template which is Present as shown below image

1.png?w=976

Adding Blazor App 

Here Open Add New Project wizard and select the Blazor App which will be the server-side Blazor App like in the image below.

2.png?w=986

Implementing gRPC Service

 We have seen how we have set up the gRPC service project and then the client-side blazor app now in this section let’s implement the service and the basic CRUD operation with it. As we all know gRPC uses ProtoBuff ( Protocol Buffers ) as an Interface Definition language for defining services and structure of the payload message. In our case, we are going to implement the ToDo app which will perform all basic ToDo CRUD operations. The first step in implementing the gRPC service is to implement the protobuff file lets see step by step how we can manipulate the ProtoBuff file

 Add New ProtoBuff File 

To add new Proto file Right-click on the ToDoGrpc Service Project it will open add new Item wizard lets search for protocol buffer option Let’s name it ToDo.Proto

3.png?w=942

Before compiling this we need to make sure are we open the .csProj file of your project and make the following changes once so that our proto file will be compiled and it will generate the stub accordingly

<ItemGroup> 
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" /> 
<Protobuf Include="Protos\ToDo.proto" GrpcServices="Server" /> 
<Protobuf Include="Protos\Sample.proto" GrpcServices="Server" /> 
</ItemGroup> 

With these two changes, we are set for developing our services in the following steps 

Defining Protocol Format 

When we have added ToDo.Proto file we will have the following structure like below  

syntax = "proto3"
option csharp_namespace = "ToDoGrpcService"
import "google/protobuf/empty.proto"
package ToDo; 
service ToDoService{ 
rpc GetToDo(google.protobuf.Empty) returns (ToDoItems); 
rpc GetToDoItem(ToDoQuery) returns (ToDoData ); 
rpc PostToDoItem(ToDoData) returns(ToDoPostResponse); 
rpc PutToDoItem(ToDoPutQuery) returns(ToDoPostResponse); 
rpc DeleteItem(ToDoQuery) returns (ToDoPostResponse); 
message ToDoData{ 
int32 Id=4; 
string Title=1; 
string Description=2; 
bool Status=3; 
message ToDoQuery{ 
int32 id=1; 
message ToDoItems{ 
repeated ToDoData ToDoItemList =1; 
message ToDoPutQuery{ 
ToDoData ToDoDataItem=1; 
int32 Id=2; 
message ToDoPostResponse{ 
string StatusMessage=1; 
bool Status=2; 
int32 StatusCode=3; 

Code Explanation

Package Declaration

syntax = "proto3";   
option csharp_namespace = "ToDoGrpcService";   
import "google/protobuf/empty.proto";   
package ToDo;

Here we have to do some initial setup and import some packages. In the first line we are specifying the proto3 as a syntax for the proto file. Next is the CSharp namespace which will be the default namespace for the stub which will be generated. Next is the import section where we will be importing the empty.proto  file which we are going to use in the next section.

 Defining the Message Payload

message ToDoData{   
int32 Id=4;   
string Title=1;   
string Description=2;   
bool Status=3;   
}   
message ToDoQuery{   
int32 id=1;   
}   
message ToDoItems{   
repeated ToDoData ToDoItemList =1;   
}   
message ToDoPutQuery{   
ToDoData ToDoDataItem=1;   
int32 Id=2;   
}   
message ToDoPostResponse{   
string StatusMessage=1;   
bool Status=2;   
int32 StatusCode=3;   
}

Code Explanation

Message NameFieldsDescriptionToDoDataId,TitleDescription,StatusThis is the basic format for the ToDo list where Id , Title Description and status will be held.ToDoQueryidThis data structure will be used while querying the ToDo ItemToDoItemsRepeated ToDoDataThis will be a list of the ToDo Data ItemsToDoPutQueryToDoData ToDoDataItemIdThis will be used in updating the ToDo Record where Id will hold the Id of the ToDo ItemToDoPostResponseStatusMessage,Status,StatusCodeThis will be the Data which will be returned after All POST Operations.

Service Declaration

service ToDoService{ 
rpc GetToDo(google.protobuf.Empty) returns (ToDoItems); 
rpc GetToDoItem(ToDoQuery) returns (ToDoData ); 
rpc PostToDoItem(ToDoData) returns(ToDoPostResponse); 
rpc PutToDoItem(ToDoPutQuery) returns(ToDoPostResponse); 
rpc DeleteItem(ToDoQuery) returns (ToDoPostResponse); 

Message Payload will define the data structure which will be passed to and from the service here we have the service definitions. 

Service NameParamsReturnsDescriptionGetToDoGoogle.Protobuff.EmptyToDoItemsIt gets all the To Do Items from the databaseGetToDoItemToDoQueryToDoDataIt will give the Item based on the Id which is passed in the QueryPostToDoItemToDoDataToDoPostResponseThis service will create new ToDo Item in DbPutToDoItemToDoPutQeryToDoPostResponseIt will update the Existing ToDo ItemDeleteItemToDoQueryToDoPostResponseDeletes the ToDoItem

 So far we have defined the services and messages. Now when these proto files are compiled successfully it will generate some stub which will be the base for the service implementation.

Service Implementations 

Below is the code which will implement the actual service

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using Google.Protobuf.WellKnownTypes; 
using Grpc.Core; 
namespace ToDoGrpcService.Services 
public class ToDoDataService : ToDoService.ToDoServiceBase 
private readonly ToDoDataContext _dataContext; 
public ToDoDataService(ToDoDataContext dataContext) 
_dataContext = dataContext; 
/// <summary> 
/// Get All Data 
/// </summary> 
/// <param name="request"></param> 
/// <param name="context"></param> 
/// <returns></returns> 
public override Task<ToDoItems> GetToDo(Empty request, ServerCallContext context) 
ToDoItems objItems = new ToDoItems(); 
foreach (var item in _dataContext.ToDoDbItems) 
objItems.ToDoItemList.Add(item); 
return Task.FromResult(objItems); 
/// <summary> 
/// Post Data  
/// </summary> 
/// <param name="request"></param> 
/// <param name="context"></param> 
/// <returns></returns> 
public override Task<ToDoPostResponse> PostToDoItem(ToDoData request, ServerCallContext context) 
_dataContext.ToDoDbItems.Add(request); 
var result = _dataContext.SaveChanges(); 
if (result>0) 
return Task.FromResult(new ToDoPostResponse() 
Status = true, 
StatusCode = 100, 
StatusMessage = "Added Successfully" 
}); 
else 
return Task.FromResult(new ToDoPostResponse() 
Status = false, 
StatusCode = 500, 
StatusMessage = "Issue Occured." 
}); 
/// <summary> 
/// Get Item with the Id 
/// </summary> 
/// <param name="request"></param> 
/// <param name="context"></param> 
/// <returns></returns> 
public override Task<ToDoData> GetToDoItem(ToDoQuery request, ServerCallContext context) 
var result = from data in _dataContext.ToDoDbItems 
where data.Id == request.Id 
select data; 
return Task.FromResult(result.First()); 
/// <summary> 
/// Deletes the Item 
/// </summary> 
/// <param name="request"></param> 
/// <param name="context"></param> 
/// <returns></returns> 
public override Task<ToDoPostResponse> DeleteItem(ToDoQuery request, ServerCallContext context) 
var item = (from data in _dataContext.ToDoDbItems 
where data.Id == request.Id 
select data).Single(); 
_dataContext.ToDoDbItems.Remove(item); 
var result = _dataContext.SaveChanges(); 
if (result > 0) 
return Task.FromResult(new ToDoPostResponse() 
Status = true, 
StatusCode = 100, 
StatusMessage = "Deleted Successfully" 
}); 
else 
return Task.FromResult(new ToDoPostResponse() 
Status = false, 
StatusCode = 500, 
StatusMessage = "Issue Occured." 
}); 
/// <summary> 
/// Updates the item 
/// </summary> 
/// <param name="request"></param> 
/// <param name="context"></param> 
/// <returns></returns> 
public override Task<ToDoPostResponse> PutToDoItem(ToDoPutQuery request, ServerCallContext context) 
_dataContext.ToDoDbItems.Update(request.ToDoDataItem); 
var result = _dataContext.SaveChanges(); 
if (result > 0) 
return Task.FromResult(new ToDoPostResponse() 
Status = true, 
StatusCode = 100, 
StatusMessage = "Updated  Successfully " 
}); 
else 
return Task.FromResult(new ToDoPostResponse() 
Status = false, 
StatusCode = 500, 
StatusMessage = "Issue Occured." 
}); 

Code Explanation 

Here we are adding a class ToDoDataService.cs which is getting inherited from the ToDoService.ToDoServiceBase class which is nothing but the stub generated from our proto file which will have the definition of the services which we have defined in the Proto file. Next we have implemented all the methods in the Proto file which will perform the basic operations which we perform normally. 

Startup Changes

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using Microsoft.AspNetCore.Builder; 
using Microsoft.AspNetCore.Hosting; 
using Microsoft.AspNetCore.Http; 
using Microsoft.EntityFrameworkCore; 
using Microsoft.Extensions.DependencyInjection; 
using Microsoft.Extensions.Hosting; 
using ToDoGrpcService.Services; 
namespace ToDoGrpcService 
public class Startup 
// This method gets called by the runtime. Use this method to add services to the container. 
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 
public void ConfigureServices(IServiceCollection services) 
services.AddGrpc(); 
services.AddDbContext<ToDoDataContext>(options =>options.UseInMemoryDatabase("ToDoDatabase")); 
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,ToDoDataContext ctx) 
if (env.IsDevelopment()) 
app.UseDeveloperExceptionPage(); 
new ToDoGenerator(ctx).ToDoDataSeed(); 
app.UseRouting(); 
app.UseEndpoints(endpoints => 
endpoints.MapGrpcService<GreeterService>(); 
endpoints.MapGrpcService<ToDoDataService>(); 
endpoints.MapGet("/", async context => 
await context.Response.WriteAsync("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"); 
}); 
}); 
}

Here a couple of changes are needed to get our service up and running. Map the endpoints to the gRPC service. In the first method, ConfigureServices, we are adding the gRPC Services in the collection and for our demo purpose we are using InMemoryDatabase so we are configuring the DbContext to use the InMemory Database The next set of Changes is needed at the Configure Method where we will do Some Database seeding which will add the initial items in the database. The next change is at the map endpoints to the gRPC service. Here we are mapping the gRPC service implementation ToDoDataService to the endpoints. Now we have implemented the Service. Let’s try to implement the Client app which will be our Blazor server-side app. In the first project section we have added one Blazor project now let’s see the basic components which will operate for us. Follow the below step to get our app working 

Initial setup and Nuget packages 

Once we are done with adding a project we need to add some NuGet packages which will help in implementing the clients for the gRPC Nuget Package which will be needed will be like below,

  1. Google.ProtoBuff
  2. Grpc.Net.Client
  3. Grpc.Tools

Once we are done with the packages let’s configure our client to compile and include the proto files 

Adding Proto File and Configure gRPC Client

 gRPC Clients are nothing but concrete types which are generated from the Proto files to generate the gRPC Client. Let’s add our proto files in the Project; for that let us make a folder Called Proto. Once they are added in the Project let’s change and configure the Proto files to be compiled as a Client. In the service section we have made them be compiled as a service so let’s modify our .csproj file and make them a client like below.

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

In this Section we have modified and made sure we are compiling our proto files as a client. How we can implement the client? Let’s see while developing the services. 

Add New Razor Component 

Right-click on project -> Add New Item ->> Add New Razor Component. Name it ToDoOperation.razor like the image below.

4.png?w=940

Code for the Razor Component will be like below

@page "/Todo" 
@inherits BlazorClient.CodeFiles.ToDoOperation 
@if (toDoItems == null && toDoItems.ToDoItemList == null
<p><em>Loading...</em></p> 
<div style="background-color:#3a0647;color:white;font-family:Calibri;font-size:x-large;font-weight:500;text-align:center;border-radius:10px"
To Do List 
</div> 
<hr style="color:#5c116f" /> 
<div class="row" style="padding-left:900px;padding-bottom:4px"
<button id="btnAdd" @onclick="ShowAddpopup" style="background-color:#5c116f;color:white;font-family:Calibri">Add New ToDo</button> 
</div> 
@if (toDoItems != null && toDoItems.ToDoItemList != null
<div class="row" style=" text-align:center; background-color:#5c116f;color:white;font-family:Calibri;font-size:larger; 
border-radius:7px; font-weight:500"> 
<div class="col-sm-2">Sr. No.</div> 
<div class="col-sm-2">Title</div> 
<div class="col-sm-2">Description</div> 
<div class="col-sm-2">Status</div> 
</div> 
@for (int i = 0; i < toDoItems?.ToDoItemList?.Count; i++) 
var a = @toDoItems.ToDoItemList[i].Id; 
<div class="row" style="text-align:center;font-family:Calibri;font-size:medium;font-weight:500;padding:1px"
<div class="col-sm-2">@(i+1)</div> 
<div class="col-sm-2">@toDoItems.ToDoItemList[i].Title</div> 
<div class="col-sm-2">@toDoItems.ToDoItemList[i].Description</div> 
<div class="col-sm-2">@toDoItems.ToDoItemList[i].Status</div> 
<div class="col-sm-2" style="text-align:left"
<button class="btn btn-primary" @onclick="@(async () => await ShowEditForm(a))">Edit</button> 
</div> 
<div class="col-sm-2" style="text-align:left"> <button class="btn btn-danger" @onclick="@(async () =>  ShowDeletePopup(a.ToString()))">Remove</button> </div> 
</div> 
else 
<div class="row" style="text-align:center;font-family:Calibri;font-size:medium;font-weight:500;padding:1px"
<h4> No To Do Item Found !! </h4> 
</div> 
@if (ShowModel == true
<div class="modal" tabindex="-1" style="display:block;" role="dialog"
<div class="modal-dialog"
<div class="modal-content"
<div class="modal-header" style="background-color:#5c116f;color:white;height:50px"
<span class="modal-title">@PopupTitle</span> 
<button type="button" class="close" @onclick="DismissPopup"
<span aria-hidden="true" style="color:white;">X</span> 
</button> 
</div> 
<div class="modal-body"
<table border="0" cellspacing="1"
<tr> 
<td><strong>Title</strong></td> 
<td><input type="text" @bind="ToDoDataItem.Title" maxlength="20" /></td> 
</tr> 
<tr> 
<td><strong>Description</strong></td> 
<td><input type="text" @bind="ToDoDataItem.Description" maxlength="20" /></td> 
</tr> 
<tr> 
<td><strong>Status</strong></td> 
<td><input type="checkbox" @bind="ToDoDataItem.Status" /></td> 
</tr> 
<tr> 
<td colspan="2" align="center"><button class="btn btn-primary" id="btnPostData" @onclick="PostData">@ActionText</button></td> 
</tr> 
</table> 
</div> 
</div> 
</div> 
</div> 
@if (ShowAlert == true
<div class="modal" tabindex="-2" style="display:block;padding-top:-200px;padding-right:0px" role="dialog"
<div class="modal-dialog"
<div class="modal-content"
<div class="modal-header" style="background-color:#5c116f;color:white;height:50px"
<span class="modal-title">Notification</span> 
<button type="button" class="close" @onclick="DismissPopup"
<span aria-hidden="true" style="color:white;">X</span> 
</button> 
</div> 
<div class="modal-body"
@OperationStatusText 
</div> 
</div> 
</div> 
</div> 
@if (ShowModeletePopup == true
<div class="modal" tabindex="-3" style="display:block;padding-top:300px" role="dialog"
<div class="modal-dialog"
<div class="modal-content"
<div class="modal-header" style="background-color:#5c116f;color:white;height:50px"
<span class="modal-title">Status</span> 
<button type="button" class="close" @onclick="DismissPopup"
<span aria-hidden="true" style="color:white;">X</span> 
</button> 
</div> 
<div class="modal-body"
<table> 
<tr> 
<td colspan="2"
Are you sure you want to delete this ToDo Item with Id @DeleteItemId ? 
</td> 
</tr> 
<tr> 
<td align="right"><button class="btn btn-primary" @onclick="DeleteData">Ok</button></td> 
<td align="left"><button class="btn btn-danger">Cancel</button></td> 
</tr> 
</table> 
</div> 
</div> 
</div> 
</div> 

Add Component Class

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using Grpc.Net.Client; 
using Microsoft.AspNetCore.Components; 
namespace BlazorClient.CodeFiles 
public partial class ToDoOperation : ComponentBase 
public bool ShowModel = false
public bool ShowAlert = false
public bool ShowModeletePopup = false
public string OperationStatusText = ""
public string PopupTitle = ""
public BlazorClient.Data.ToDoDataItem ToDoDataItem = null
public string ActionText = ""
public ToDoGrpcService.ToDoItems toDoItems; 
public string DeleteItemId { get; set; } 
[Inject] 
protected BlazorClient.Services.ToDoDataService ToDoService { get; set; }  
protected override void OnInitialized() 
GetToDoList(); 
protected void GetToDoList() 
toDoItems= ToDoService.GetToDoList(); 
protected async Task ShowEditForm(int Id) 
PopupTitle = "To Do Edit"
ActionText = "Update"
ToDoDataItem = ToDoService.GetToDoItem(Id); 
ShowModel = true
protected void ShowAddpopup() 
ToDoDataItem = new Data.ToDoDataItem() { Title = "", Description = "", Status = false, Id = 0 }; 
PopupTitle = "To Do Add"
ActionText = "Add"
ShowModel = true
protected void ShowDeletePopup(string Id) 
DeleteItemId = Id; 
ShowModeletePopup = true
protected void PostData() 
bool status = false
if (ToDoDataItem.Id > 0) 
status = ToDoService.UpdateToDoData(this.ToDoDataItem); 
else 
status = ToDoService.AddToDoData(this.ToDoDataItem); 
Reload(status); 
public void DeleteData() 
var operationStatus = ToDoService.DeleteData(DeleteItemId); 
Reload(operationStatus); 
protected void Reload(bool status) 
ShowModeletePopup = false
ShowModel = false
GetToDoList(); 
ShowAlert = true
if (status) 
OperationStatusText = "Processed Successfully !! "
else 
OperationStatusText = "Error Occured  "
protected void DismissPopup() 
ShowModel = false
ShowAlert = false
ShowModeletePopup = false

Here in our code-behind code, we have injected a service ToDoDataService which will hold all our gRPC operations. 

Design Service to Call gRPC Methods 

Code for the ToDoData Service will be like below.

using Grpc.Net.Client; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
namespace BlazorClient.Services 
public class ToDoDataService 
private ToDoGrpcService.ToDoService.ToDoServiceClient GetServiceClient() 
var channel = GrpcChannel.ForAddress("https://localhost:5001"); 
return new ToDoGrpcService.ToDoService.ToDoServiceClient(channel); 
public bool AddToDoData(Data.ToDoDataItem toDoDataItem) 
var client = GetServiceClient(); 
var todoData = new ToDoGrpcService.ToDoData() 
Status = toDoDataItem.Status, 
Title = toDoDataItem.Title, 
Description = toDoDataItem.Description 
}; 
var response = client.PostToDoItem(todoData, null); 
return response.Status; 
public bool UpdateToDoData(Data.ToDoDataItem toDoDataItem) 
var client = GetServiceClient(); 
var updateData = new ToDoGrpcService.ToDoPutQuery(); 
updateData.Id = toDoDataItem.Id; 
updateData.ToDoDataItem = new ToDoGrpcService.ToDoData() 
Id = toDoDataItem.Id, 
Status = toDoDataItem.Status, 
Title = toDoDataItem.Title, 
Description = toDoDataItem.Description 
}; 
var response = client.PutToDoItem(updateData, null); 
return response.Status; 
public bool DeleteData(string ToDoId) 
var client = GetServiceClient(); 
var response = client.DeleteItem(new ToDoGrpcService.ToDoQuery() { Id = Convert.ToInt32(ToDoId) }, null); 
return response.Status; 
public ToDoGrpcService.ToDoItems GetToDoList() 
var client = GetServiceClient(); 
return  client.GetToDo(new Google.Protobuf.WellKnownTypes.Empty(), null); 
public Data.ToDoDataItem GetToDoItem(int id) 
var client = GetServiceClient(); 
var todoItem = client.GetToDoItem(new ToDoGrpcService.ToDoQuery() { Id = Convert.ToInt32(id) }, null); 
return new Data.ToDoDataItem() { Title = todoItem.Title, Description = todoItem.Description, Status = todoItem.Status, Id = todoItem.Id }; 

Here as you can see we have added all our methods like Add, Update, Delete Get Methods which will call their operations accordingly. Here we are creating the gRPC Client and that will be used to call all the operations. 

Add Service in the Service Collection 

To make sure service is available in the application let’s add the service in the collection with a single line like below.

services.AddSingleton<BlazorClient.Services.ToDoData

Once we have all this implemented we will have an output like below,

finaloutput.gif?w=600

You can find all this source code here Source code 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK