{"id":87072,"date":"2020-04-29T19:56:44","date_gmt":"2020-04-29T19:56:44","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=87072"},"modified":"2022-05-02T20:39:24","modified_gmt":"2022-05-02T20:39:24","slug":"integrating-asp-net-core-with-grpc","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/integrating-asp-net-core-with-grpc\/","title":{"rendered":"Integrating ASP.NET Core with gRPC"},"content":{"rendered":"<p>Have you ever dealt with <a href=\"https:\/\/en.wikipedia.org\/wiki\/Remote_procedure_call\"><strong>RPC<\/strong><\/a>? It stands for <em>Remote Procedure Call<\/em>, a famous term from distributed computing for when you need to call a service routine from one machine to another . It\u2019s like the basics for anyone who wishes to create REST\/SOAP requests or GraphQL architectures. You need to understand a bunch of concepts like parallelism, clusters, network latency, failover and fallbacks, just to get started. This article discusses integrating ASP.NET Core with gRPC.<\/p>\n<p>Although it\u2019s an interesting topic to discuss, that\u2019s not the focus here. I\u2019ll talk about <a href=\"https:\/\/grpc.io\/\">gRPC<\/a>, the official Google\u2019s modernization of the protocol into a powerful framework. It is modern, open source and can run in any type of environment. It adds inherent load balancing, tracing, health checking and authentication (this one, by the way, is required right at the beginning).<\/p>\n<p>Before you start thinking that Google wants to \u201cremake\u201d the web, since you already have HTTP and web services to provide such features, gRPC goes a bit beyond. It\u2019s based in HTTP\/2 which, automatically, says it\u2019s much faster. Besides that, Google redesigned it in a way speed is at the top of the priority list.<\/p>\n<p>In always-needy architectures like those huge companies (like Netflix, Cisco and Google itself) have, efficiency is a must. Microservices and distributed systems may communicate with each other through protocols and channels that should be fast.<\/p>\n<p>Think about it: if you choose RESTful, there\u2019s a couple of design patterns and rules you need to obey. In turn, you\u2019re going to be working under HTTP (verbose) and making hundreds of calls because your code design can\u2019t be messed up by ugly endpoints. While there are already have options like GraphQL, for example, does it really need to communicate with HTTP-only?<\/p>\n<p>If your client and server applications are made by you (or your company), and there\u2019s no need to expose it out there (still, options would be available through public Web Services, API Gateway, etc.), why not?<\/p>\n<p>It\u2019s faster, available to 10 programming languages (including web, i.e., JavaScript into Docker images), bi-directional (for cases where you need constant data flowing through streams from both sides) and so much more.<\/p>\n<p>All the magic happens with <a href=\"https:\/\/developers.google.com\/protocol-buffers\/\">Protocol Buffers<\/a> and stubs. Of course, protobuf is Google\u2019s as well. It\u2019s their language\/platform-neutral mechanism for data serialization and deserialization. The good news is that you\u2019re not stuck into the proto universe, gRPC is <a href=\"https:\/\/grpc.io\/blog\/grpc-with-json\/\">encoding agnostic too<\/a>, which means that JSON, XML, etc. can also be used to serialize the data if you prefer so.<\/p>\n<p>Like everything else, there are pros and cons. While it is good to have the familiar JSON syntax, protobuf allows validations, data types, service creation. On the other hand, it is not human readable, hard to decode and takes more time to process compared to JSON.<\/p>\n<p>This is how a gRPC workflow takes place:<\/p>\n<p>Protobufs &gt; protoc &gt; generated code\/stubs &gt; client\/server<\/p>\n<p>First, you create your proto files, defining not only the data structure, types and validations but also the services to be implemented by the server app along with the contract to be followed by the client stubs.<\/p>\n<p>Then, depending on the language and platform you\u2019re using, and after you compile it (<em>protoc<\/em>, the proto compiler), some code is going to be autogenerated, based on those definitions. After that, you can create your server and client code.<\/p>\n<p>It\u2019s time to stop talking now and move to action. For this article, I\u2019ll demonstrate how to create a gRPC server based in a proto contract, as well as two clients: one for a Console App and another for a fully-functional web application (made with ASP.NET Web MVC), for you to check how this type of integration works for the two worlds.<\/p>\n<h2>Setup<\/h2>\n<p>Please, make sure you have the latest version of Visual Studio (Community Edition). When installing it, check the <em>ASP.NET and web development<\/em> workload. If you already have VS installed, but not this workload, please refer to this <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/install\/modify-visual-studio?view=vs-2019\">link<\/a>. Finally, <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet-core\/3.0\">.NET Core 3.0 SDK<\/a> (or later) is also needed.<\/p>\n<p>There\u2019s no need to install anything related to gRPC. Visual Studio already has presets for it, and the rest are NuGet dependencies.<\/p>\n<p>Open Visual Studio and go to \u201c<em>Create a new project<\/em>\u201d option. (The completed project can be found <a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/04\/grpc-repos.zip\">here<\/a>.)Then, search for the \u201c<em>grpc<\/em>\u201d term and select the option like below:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"850\" class=\"wp-image-87073\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/04\/tela-de-celular-com-publicacao-numa-rede-social-d.png\" alt=\"Tela de celular com publica\u00e7\u00e3o numa rede social\n\nDescri\u00e7\u00e3o gerada automaticamente\" \/><\/p>\n<p><strong>Figure 1. Creating a new gRPC service project<\/strong><\/p>\n<p>Click <em>Next<\/em>. Then, type the name of the project like in Figure 2, select the directory of preference and click <em>Create<\/em>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"850\" class=\"wp-image-87074\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/04\/tela-de-celular-com-texto-preto-sobre-fundo-branco.png\" alt=\"Tela de celular com texto preto sobre fundo branco\n\nDescri\u00e7\u00e3o gerada automaticamente\" \/><\/p>\n<p><strong>Figure 2. Giving the project a name<\/strong><\/p>\n<p>For the next screen, select the only available option \u201cgRPC Service\u201d and, finally, click the <em>Create<\/em> button.<\/p>\n<p>Take a look at the generated files:<\/p>\n<ul>\n<li><em>greet.proto<\/em>: this is the protobuf file I mentioned earlier. Here, you get to see:\n<ul>\n<li>The <em>syntax<\/em>. It\u2019s important because it tells which version of protobuf syntax is used in the file.<\/li>\n<li>The optional namespace for the C# service. This option comes just because it\u2019s using the VS gRPC template. Giving a proper namespace is important since the autogenerated code reflects this setting.<\/li>\n<li>The service, <em>Greeter<\/em>. This config sets the methods (types, params and returns) of the interface contract. It\u2019s here where you say how clients and servers communicate with each other.<\/li>\n<li>The types. For each different object that is not a primitive type, you need to declare it as a <em>message<\/em>. It is like the <em>class<\/em> you\u2019re used to. Note also that each message\u2019s attribute must receive a type (primitive or another message), a name and a number. The number refers to the order of that attribute in the message.<\/li>\n<\/ul>\n<\/li>\n<li>The <em>Services<\/em> folder: the implementation, also autogenerated, of each service declared in the proto.<\/li>\n<li>The rest of the files are just the common ones.<\/li>\n<\/ul>\n<p>Have a look at <em>Startup.cs<\/em>. There, the methods to configure the services add gRPC services to the service collection, mapping to the generated service class. You don\u2019t need to change anything here.<\/p>\n<p>Now move to the Console App client. Open a new instance of Visual Studio and create a new project again. This time, a <em>Console App (.NET Core)<\/em>. Click <em>Next<\/em>, enter a name for the project and click <em>Create<\/em>.<\/p>\n<p>Nothing new. However, note that no gRPC template was applied to this project. It means that you still need to add the dependencies via NuGet. For that, open the <em>Package Manager Console<\/em> (via <em>Tools &gt; NuGet Package Manager<\/em>) and run the following command:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Install-Package Grpc.Net.Client\r\nInstall-Package Google.Protobuf\r\nInstall-Package Grpc.Tools<\/pre>\n<p>Those are important because they implement the client for gRPC in .NET Core projects, as well as the API for protobuf handling in C# and the autogenerating code features.<\/p>\n<p>Since the proto file is the contract between both worlds, you need to copy the <em>greet.proto<\/em> (along with the folder) generated in the server project to this one. Then, edit the <em>.csproj<\/em> file by right-clicking the project and, then, \u201c<em>Edit Project file<\/em>\u201d. Add the following:<\/p>\n<pre class=\"lang:c# theme:vs2012\">&lt;ItemGroup&gt;\r\n  &lt;Protobuf Include=\"Protos\\greet.proto\" GrpcServices=\"Client\" \/&gt;\r\n&lt;\/ItemGroup&gt;<\/pre>\n<p>That reference is important so the gRPC tools can autogenerate code by the proto file. For that, after everything\u2019s saved, build the project. No code file is visible in the project tree; they\u2019ll be in the background.<\/p>\n<p>Now, update your <em>Program.cs<\/em> file to the following:<\/p>\n<p><strong>Listing 1. Calling the gRPC service.<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">using System;\r\nusing Grpc.Net.Client;\r\nusing SimpleTalkGrpcService;\r\nnamespace GrpcGreeterClient\r\n{\r\n    class Program\r\n    {\r\n        static async Task Main(string[] args)\r\n        {\r\n            using var channel = GrpcChannel.ForAddress(\"https:\/\/localhost:5001\");\r\n            var client = new Greeter.GreeterClient(channel);\r\n            var reply = await client.SayHelloAsync(\r\n                              new HelloRequest { Name = \"GreeterClient\" });\r\n            Console.WriteLine(\"Greeting: \" + reply.Message);\r\n            Console.WriteLine(\"Press any key to exit...\");\r\n            Console.ReadKey();\r\n        }\r\n    }\r\n}<\/pre>\n<p>Everything works around the <code>GrpcChannel<\/code> class. It opens the channel (if it is on, obviously) and returns the channel to that connection. Now, it\u2019s time to make use of the autogenerated object via <code>Greeter<\/code> class: you instantiate its service constructor by passing the channel as a parameter. The rest is just methods and attributes created in the proto before being used in C# code.<\/p>\n<p>In order to test, just start first the service and wait for it to print the info messages at the console, then start the client. You\u2019ll see the following message:<\/p>\n<pre class=\"lang:none theme:none\">Greeting: Hello GreeterClient\r\nPress any key to exit...<\/pre>\n<p>Congrats! This is your first client-server example working.<\/p>\n<p>Simple, isn\u2019t it? Time to complicate a bit more with a web application involved.<\/p>\n<h2>Creating the ASP.NET example<\/h2>\n<p>The second example includes a web page that is going to be managed by jQuery and Ajax calls. Even though it is now possible to work with gRPC calls from the web browser, the backend of the API (made in ASP.NET) will handle that. First, because you can get to see how an API can orchestrate remote procedure calls and, second, because this part of the gRPC framework still has some limitations.<\/p>\n<p>However, before proceeding, create the proto file. This is the one which defines the methods and signatures for the CRUD of products. It is the contract ruler, so neither the client nor the service can do anything without the proper protobuf settings.<\/p>\n<p>Go back to the services project. Listing 2 shows how it\u2019ll look. Make sure to add the code after creating the <em>product.proto<\/em> file into the existing <em>Protos<\/em> folder.<\/p>\n<p><strong>Listing 2. The <em>product.proto<\/em> file.<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">syntax = \"proto3\";\r\noption csharp_namespace = \"ProductsService\";\r\npackage products;\r\nservice ProductService {\r\n    rpc GetAll (Empty) returns (ProductList) {}\r\n    rpc Get (ProductId) returns (Product) {}\r\n    rpc Insert (Product) returns (Product) {}\r\n    rpc Update (Product) returns (Product) {}\r\n    rpc Delete (ProductId) returns (Empty) {}\r\n}\r\nmessage Empty {}\r\nmessage Product {\r\n    int32 productId = 1;\r\n    string name = 2;\r\n    int32 amount = 3;\r\n    string brand = 4;\r\n    float value = 5;\r\n}\r\nmessage ProductList {\r\n    repeated Product products = 1;\r\n}\r\nmessage ProductId {\r\n    int32 id = 1;\r\n}<\/pre>\n<p>Some things are new this time. The service is wider, with more methods than the previous version. Most of them deal with new <em>messages<\/em> as well which, in turn, have new attributes (like <em>int32<\/em> for integers and <em>float<\/em> for decimal numbers). Go ahead and have a look over the <a href=\"https:\/\/developers.google.com\/protocol-buffers\/docs\/proto3\">possible data types for protobufs<\/a>.<\/p>\n<p>Since you\u2019re already in the server project, take this chance to update the project to the CRUD needs. It\u2019s going to be an in-memory CRUD; it means that you\u2019ll maintain a list of the products in the memory that\u2019ll survive until the server project stops. While it\u2019s better to store the data permanently (feel free to upgrade in such way with a database of your choice), for this example, the in-memory list is enough. The web client will be restarted from time to time, but the server is going to be up all the time, so it emulates how a database in production would look.<\/p>\n<p>Remember that Visual Studio needs to know it is a gRPC project, for client and server, too. So, again, right-click the project, go to <em>Edit Project file<\/em> and add the following:<\/p>\n<pre class=\"lang:c# theme:vs2012\">&lt;ItemGroup&gt;\r\n  &lt;Protobuf Include=\"Protos\\product.proto\" GrpcServices=\"Server\" \/&gt;\r\n&lt;\/ItemGroup&gt;<\/pre>\n<p>First, change the service. You can decide to create a new one or change the current one to the shown in Listing 3.<\/p>\n<p><strong>Listing 3. The GrpcCrudService.cs code.<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing Grpc.Core;\r\nusing Microsoft.Extensions.Logging;\r\nnamespace ProductsService\r\n{\r\n    public class GrpcCrudService : ProductService.ProductServiceBase\r\n    {\r\n        private readonly List&lt;Product&gt; _products = new List&lt;Product&gt;();\r\n        private int idCount = 0;\r\n        private readonly ILogger&lt;GrpcCrudService&gt; _logger;\r\n        public GrpcCrudService(ILogger&lt;GrpcCrudService&gt; logger)\r\n        {\r\n            _logger = logger;\r\n            _products.Add(new Product()\r\n            {\r\n                ProductId = idCount++,\r\n                Name = \"Farm Flour\",\r\n                Amount = 10,\r\n                Brand = \"Bill's Corn\",\r\n                Value = 2.33f\r\n            });\r\n        }\r\n        public override Task&lt;ProductList&gt; GetAll(Empty empty, ServerCallContext context)\r\n        {\r\n            ProductList pl = new ProductList();\r\n            pl.Products.AddRange(_products);\r\n            return Task.FromResult(pl);\r\n        }\r\n        public override Task&lt;Product&gt; Get(ProductId productId, ServerCallContext context)\r\n        {\r\n            return Task.FromResult( \/\/\r\n                (from p in _products where p.ProductId == productId.Id select p).FirstOrDefault());\r\n        }\r\n        public override Task&lt;Product&gt; Insert(Product product, ServerCallContext context)\r\n        {\r\n            product.ProductId = idCount++;\r\n            _products.Add(product);\r\n            return Task.FromResult(product);\r\n        }\r\n        public override Task&lt;Product&gt; Update(Product product, ServerCallContext context)\r\n        {\r\n            var productToUpdate = (from p in _products where p.ProductId == product.ProductId select p).FirstOrDefault();\r\n            if (productToUpdate != null)\r\n            {\r\n                productToUpdate.Name = product.Name;\r\n                productToUpdate.Amount = product.Amount;\r\n                productToUpdate.Brand = product.Brand;\r\n                productToUpdate.Value = product.Value;\r\n                return Task.FromResult(product);\r\n            }\r\n            return Task.FromException&lt;Product&gt;(new EntryPointNotFoundException());\r\n        }\r\n        public override Task&lt;Empty&gt; Delete(ProductId productId, ServerCallContext context)\r\n        {\r\n            var productToDelete = (from p in _products where p.ProductId == productId.Id select p).FirstOrDefault();\r\n            if (productToDelete == null)\r\n            {\r\n                return Task.FromException&lt;Empty&gt;(new EntryPointNotFoundException());\r\n            }\r\n            _products.Remove(productToDelete);\r\n            return Task.FromResult(new Empty());\r\n        }\r\n    }\r\n}<\/pre>\n<p>First, and most importantly, note the extends made in the class declaration. After you save everything and rebuild the project, the autogenerated classes are re-generated. Don\u2019t forget to update the extends alike. The namespace must also change according to what\u2019s informed in the proto file. You\u2019re creating a product and adding it to the initial list, in order to have some data for testing.<\/p>\n<p>The rest are just methods corresponding to each one of the declarations made in the proto. Since gRPC works with an async API, you must provide signatures that attend the same (by the use of <code>System.Threading.Tasks.Task<\/code> class). The manipulation over the list makes use of <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/concepts\/linq\/\">Linq<\/a>, to simplify your life. Plus, for the cases where the given id was not found in the list, the code throws exceptions via <code>FromException<\/code> method, provided by <em>Task<\/em> class. Feel free to customize with your own exceptions.<\/p>\n<p>Note that, for this part of the code, you\u2019re also using the same classes declared in the proto, because they were re-generated. In case your VS can\u2019t find them, comment any line of code with compilation errors and try to build the project again. Repeat it until the proto classes become recognizable by Visual Studio.<\/p>\n<p>Final changes belong to the <code>Startup<\/code> class. Open it and change the name of the service in the generics of the <code>MapGrpcService<\/code> method. Change it to <code>GrpcCrudService<\/code>, the new service. Also, add the following to the <code>ConfigureServices<\/code> method:<\/p>\n<pre class=\"lang:c# theme:vs2012\">services.AddSingleton&lt;GrpcCrudService&gt;();<\/pre>\n<p>And the respective import at the beginning of the file:<\/p>\n<pre class=\"lang:c# theme:vs2012\">using ProductsService;<\/pre>\n<p>You need this to transform the service in a singleton, in order to maintain the list and its values every time a new request arrives. Otherwise, the list would be created for each request, since each request represented a new instance of the service.<\/p>\n<p>Move on to the web project. For this, open a new window of Visual Studio and create another project. Search for <em>ASP.NET Core Web<\/em> and select it like in Figure 3.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"850\" class=\"wp-image-87075\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/04\/tela-de-computador-com-texto-preto-sobre-fundo-bra.png\" alt=\"Tela de computador com texto preto sobre fundo branco\n\nDescri\u00e7\u00e3o gerada automaticamente\" \/><\/p>\n<p><strong>Figure 3. Creating a new ASP.NET Core Web Application.<\/strong><\/p>\n<p>Click <em>Next<\/em>. Give it the name of <em>SimpleTalkGrpcWebClient<\/em> and click <em>Create<\/em>.<\/p>\n<p>In the next screen, make sure to select the options <em>.NET Core<\/em> and <em>ASP.NET Core 3.1<\/em> right at the beginning. Then, locate the option <em>Web Application (Model-View-Controller)<\/em>, as you see in Figure 4. Regarding the HTTPS configuration, it\u2019s ok to leave it selected unless you want to keep only HTTP.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"710\" class=\"wp-image-87076\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/04\/tela-de-celular-com-texto-preto-sobre-fundo-branco-1.png\" alt=\"Tela de celular com texto preto sobre fundo branco\n\nDescri\u00e7\u00e3o gerada automaticamente\" \/><\/p>\n<p><strong>Figure 4. Selecting options for the web application.<\/strong><\/p>\n<p>The project is created. If you\u2019re not familiar with MVC in ASP.NET (which is not the focus of this article), I\u2019d advise you to read <a href=\"https:\/\/dotnet.microsoft.com\/apps\/aspnet\/mvc\">this<\/a>.<\/p>\n<p>Again, the proto file is the most important so, first, make sure to copy the folder <em>Protos<\/em> from the server project to this one.<\/p>\n<p>Then, run the following commands to install all the needed NuGet dependencies:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Install-Package Grpc.Net.Client\r\nInstall-Package Google.Protobuf\r\nInstall-Package Grpc.Tools<\/pre>\n<p>Those are the only ones you need since the nature of the project already injected the <em>Microsoft.NETCore.App<\/em> and <em>Microsoft.AspNetCore.App<\/em> frameworks for the API and MVC files.<\/p>\n<p>Then, edit the project (<em>right-click at the project &gt; Edit Project File<\/em>) and add the following:<\/p>\n<pre class=\"lang:c# theme:vs2012\">&lt;ItemGroup&gt;\r\n  &lt;Protobuf Include=\"Protos\\product.proto\" GrpcServices=\"Client\" \/&gt;\r\n&lt;\/ItemGroup&gt;<\/pre>\n<p>Rebuild it. Visual Studio has already created a controller, error model and a basic home view made of a good templating system. Leave them as they are. The <em>wwwroot<\/em> also stores CSS and JavaScript files, along with jQuery and Bootstrap (which you\u2019ll use for the CRUD page). Go ahead and explore a bit the files; it\u2019s very straightforward.<\/p>\n<p>Start by the <em>ProductsController.cs<\/em>. In the <em>Controllers<\/em> folder, create this class and add the code in Listing 4.<\/p>\n<p><strong>Listing 4. Creating the ProductsController class.<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">using Grpc.Net.Client;\r\nusing Microsoft.AspNetCore.Mvc;\r\nusing ProductsService;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nnamespace SimpleTalkGrpcWebClient.Controllers\r\n{\r\n\t[ApiController]\r\n\t[Route(\"api\/[controller]\")]\r\n\tpublic class ProductsController : Controller\r\n\t{\r\n\t\tprivate readonly GrpcChannel channel;\r\n\t\tpublic ProductsController()\r\n\t\t{\r\n\t\t\tchannel = GrpcChannel.ForAddress(\"https:\/\/localhost:5001\");\r\n\t\t}\r\n\t\t[HttpGet]\r\n\t\tpublic List&lt;Product&gt; GetAll()\r\n\t\t{\r\n\t\t\tvar client = new ProductService.ProductServiceClient(channel);\r\n\t\t\treturn client.GetAll(new Empty()).Products.ToList();\r\n\t\t}\r\n\t\t[HttpGet(\"{id}\", Name = \"GetProduct\")]\r\n\t\tpublic IActionResult GetById(int id)\r\n\t\t{\r\n\t\t\tvar client = new ProductService.ProductServiceClient(channel);\r\n\t\t\tvar product = client.Get(new ProductId { Id = id });\r\n\t\t\tif (product == null)\r\n\t\t\t{\r\n\t\t\t\treturn NotFound();\r\n\t\t\t}\r\n\t\t\treturn new ObjectResult(product);\r\n\t\t}\r\n\t\t[HttpPost]\r\n\t\tpublic IActionResult Post([FromBody] Product product)\r\n\t\t{\r\n\t\t\tvar client = new ProductService.ProductServiceClient(channel);\r\n\t\t\tvar createdProduct = client.Insert(product);\r\n\t\t\t\r\n\t\t\treturn CreatedAtRoute(\"GetProduct\", new { id = createdProduct.ProductId }, createdProduct);\r\n\t\t}\r\n\t\t[HttpPut]\r\n\t\tpublic IActionResult Put([FromBody] Product product)\r\n\t\t{\r\n\t\t\tvar client = new ProductService.ProductServiceClient(channel);\r\n\t\t\tvar udpatedProduct = client.Update(product);\r\n\t\t\tif (udpatedProduct == null)\r\n\t\t\t{\r\n\t\t\t\treturn NotFound();\r\n\t\t\t}\r\n\t\t\treturn NoContent();\r\n\t\t}\r\n\t\t[HttpDelete(\"{id}\")]\r\n\t\tpublic IActionResult Delete(int id)\r\n\t\t{\r\n\t\t\tvar client = new ProductService.ProductServiceClient(channel);\r\n\t\t\tclient.Delete(new ProductId { Id = id });\r\n\t\t\treturn new ObjectResult(id);\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>This controller differs a bit from the <em>Home<\/em> controller. Mainly because it was made to be an API controller, not a controller to serve models and views. <em>HomeController<\/em> is important the way it is because it serves the entry point for the index HTML page.<\/p>\n<p>The REST endpoints, in turn, communicates to the gRPC service. You can also create your own client services to organize it better but stick to this structure for the sake of simplicity.<\/p>\n<p>First thing, you need the <em>channel<\/em>, remember? Create it at the controller\u2019s constructor. Then, for each request, a new client must be instantiated. The client has all the methods defined in the proto, so you\u2019ll just make use of each one in the corresponding HTTP operation.<\/p>\n<p>Move on to the CSHTML code. Open the <em>Index.cshtml<\/em>, located into <em>Views\/Home<\/em> folder, and change its content to the one in Listing 5.<\/p>\n<p><strong>Listing 5. Index.cshtml code.<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">@{\r\n    ViewData[\"Title\"] = \"Home Page\";\r\n}\r\n&lt;div class=\"container\"&gt;\r\n    &lt;div class=\"py-3 text-center\"&gt;\r\n        &lt;img class=\"d-block mx-auto mb-4\" src=\"https:\/\/www.bloorresearch.com\/wp-content\/uploads\/2019\/04\/REDGATE-logo-300x470-.png\" alt=\"\" width=\"272\"&gt;\r\n        &lt;h2&gt;Product's List&lt;\/h2&gt;\r\n        &lt;p class=\"lead\"&gt;A CRUD made with ASP.NET Core, Bootstrap and gRPC&lt;\/p&gt;\r\n    &lt;\/div&gt;\r\n    &lt;button type=\"button\" class=\"btn redgate\" data-toggle=\"modal\" data-target=\"#productModal\" onclick=\"clearStuff();\"&gt;New Product&lt;\/button&gt;&lt;br \/&gt;&lt;br \/&gt;\r\n    &lt;table class=\"table table-bordered table-striped table-hover\"&gt;\r\n        &lt;thead&gt;\r\n            &lt;tr&gt;\r\n                &lt;th&gt;\r\n                    ID\r\n                &lt;\/th&gt;\r\n                &lt;th&gt;\r\n                    Name\r\n                &lt;\/th&gt;\r\n                &lt;th&gt;\r\n                    Amount\r\n                &lt;\/th&gt;\r\n                &lt;th&gt;\r\n                    Brand\r\n                &lt;\/th&gt;\r\n                &lt;th&gt;\r\n                    Value\r\n                &lt;\/th&gt;\r\n                &lt;th&gt;\r\n                    Actions\r\n                &lt;\/th&gt;\r\n            &lt;\/tr&gt;\r\n        &lt;\/thead&gt;\r\n        &lt;tbody class=\"tbody\"&gt;\r\n        &lt;\/tbody&gt;\r\n    &lt;\/table&gt;\r\n&lt;\/div&gt;\r\n&lt;div class=\"modal fade\" id=\"productModal\" role=\"dialog\"&gt;\r\n    &lt;div class=\"modal-dialog\"&gt;\r\n        &lt;div class=\"modal-content\"&gt;\r\n            &lt;div class=\"modal-header\"&gt;\r\n                &lt;h4 class=\"modal-title\"&gt;Add Product&lt;\/h4&gt;\r\n                &lt;button type=\"button\" class=\"close\" data-dismiss=\"modal\"&gt;\u00d7&lt;\/button&gt;\r\n            &lt;\/div&gt;\r\n            &lt;div class=\"modal-body\"&gt;\r\n                &lt;form&gt;\r\n                    &lt;div class=\"form-group\"&gt;\r\n                        &lt;label for=\"ProductId\"&gt;ID&lt;\/label&gt;\r\n                        &lt;input type=\"text\" class=\"form-control\" id=\"ProductID\" placeholder=\"Id\" disabled=\"disabled\" \/&gt;\r\n                    &lt;\/div&gt;\r\n                    &lt;div class=\"form-group\"&gt;\r\n                        &lt;label for=\"Name\"&gt;Name&lt;\/label&gt;\r\n                        &lt;input type=\"text\" class=\"form-control\" id=\"Name\" placeholder=\"Name\" \/&gt;\r\n                    &lt;\/div&gt;\r\n                    &lt;div class=\"form-group\"&gt;\r\n                        &lt;label for=\"Amount\"&gt;Amount&lt;\/label&gt;\r\n                        &lt;input type=\"number\" class=\"form-control\" id=\"Amount\" placeholder=\"Amount\" \/&gt;\r\n                    &lt;\/div&gt;\r\n                    &lt;div class=\"form-group\"&gt;\r\n                        &lt;label for=\"Brand\"&gt;Brand&lt;\/label&gt;\r\n                        &lt;input type=\"text\" class=\"form-control\" id=\"Brand\" placeholder=\"Brand\" \/&gt;\r\n                    &lt;\/div&gt;\r\n                    &lt;div class=\"form-group\"&gt;\r\n                        &lt;label for=\"Value\"&gt;Value&lt;\/label&gt;\r\n                        &lt;input type=\"number\" step=\"0.01\" min=\"0\" class=\"form-control\" id=\"Value\" placeholder=\"Value\" \/&gt;\r\n                    &lt;\/div&gt;\r\n                &lt;\/form&gt;\r\n            &lt;\/div&gt;\r\n            &lt;div class=\"modal-footer\"&gt;\r\n                &lt;button type=\"button\" class=\"btn redgate\" id=\"btnAddProduct\" onclick=\"return AddProduct();\"&gt;Add&lt;\/button&gt;\r\n                &lt;button type=\"button\" class=\"btn redgate\" id=\"btnUpdateProduct\" style=\"display:none;\" onclick=\"UpdateProduct();\"&gt;Update&lt;\/button&gt;\r\n                &lt;button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\"&gt;Close&lt;\/button&gt;\r\n            &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/div&gt;<\/pre>\n<p>Since the jQuery, Bootstrap and common CSS\/JS files are already imported in the <em>_Layout.cshtml<\/em> file, you only need to provide the content for the CRUD.<\/p>\n<p>This code is made basically of the table to display results from the server (the header only, because the body is going to be mounted by the JavaScript code), and a modal you\u2019ll use to add and update the data.<\/p>\n<p>Speaking of JavaScript code, copy the code presented in Listing 6 to the <em>site.js<\/em> (under the <em>wwwroot\/js<\/em> folder).<\/p>\n<p><strong>Listing 6. Content of site.js file.<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">function LoadProducts() {\r\n    $.ajax({\r\n        url: \"\/api\/products\",\r\n        type: \"GET\",\r\n        contentType: \"application\/json;charset=utf-8\",\r\n        dataType: \"json\",\r\n        success: function (result) {\r\n            var output = '';\r\n            $.each(result, function (key, item) {\r\n                output += '&lt;tr&gt;';\r\n                output += '&lt;td&gt;' + item.productId + '&lt;\/td&gt;';\r\n                output += '&lt;td&gt;' + item.name + '&lt;\/td&gt;';\r\n                output += '&lt;td&gt;' + item.amount + '&lt;\/td&gt;';\r\n                output += '&lt;td&gt;' + item.brand + '&lt;\/td&gt;';\r\n                output += '&lt;td&gt;' + item.value + '&lt;\/td&gt;';\r\n                output += `&lt;td&gt;&lt;a href=\"#\" class=\"btn redgate\" onclick=\"SetUpEditModal(${item.productId})\"&gt;Edit&lt;\/a&gt; |\r\n                        &lt;a href=\"#\" class=\"btn redgate\" onclick=\"DeleteProduct(${item.productId})\"&gt;Delete&lt;\/a&gt;&lt;\/td&gt;`;\r\n                output += '&lt;\/tr&gt;';\r\n            });\r\n            $('.tbody').html(output);\r\n        },\r\n        error: function (message) {\r\n            console.log(message.responseText);\r\n        }\r\n    });\r\n}\r\nfunction AddProduct() {\r\n    var res = validateForm();\r\n    if (res == false) {\r\n        return false;\r\n    }\r\n    var productObj = {\r\n        name: $('#Name').val(),\r\n        amount: parseInt($('#Amount').val()),\r\n        brand: $('#Brand').val(),\r\n        value: parseFloat($('#Value').val())\r\n    };\r\n    $.ajax({\r\n        url: \"\/api\/products\",\r\n        data: JSON.stringify(productObj),\r\n        type: \"POST\",\r\n        contentType: \"application\/json;charset=utf-8\",\r\n        success: function () {\r\n            LoadProducts();\r\n            $('#productModal').modal('hide');\r\n        },\r\n        error: function (message) {\r\n            console.log(message.responseText);\r\n        }\r\n    });\r\n}\r\nfunction SetUpEditModal(id) {\r\n    $('form input').css('border-color', 'grey');\r\n    $('#productModal h4').text('Edit Product');\r\n    $.ajax({\r\n        url: \"\/api\/products\/\" + id,\r\n        typr: \"GET\",\r\n        contentType: \"application\/json;charset=UTF-8\",\r\n        dataType: \"json\",\r\n        success: function (result) {\r\n            $('#ProductID').val(result.productId);\r\n            $('#Name').val(result.name);\r\n            $('#Amount').val(result.amount);\r\n            $('#Brand').val(result.brand);\r\n            $('#Value').val(result.value);\r\n            $('#productModal').modal('show');\r\n            $('#btnUpdateProduct').show();\r\n            $('#btnAddProduct').hide();\r\n        },\r\n        error: function (message) {\r\n            console.log(message.responseText);\r\n        }\r\n    });\r\n    return false;\r\n}\r\nfunction UpdateProduct() {\r\n    if (!validateForm()) {\r\n        return false;\r\n    }\r\n    var productObj = {\r\n        ProductID: parseInt($('#ProductID').val()),\r\n        Name: $('#Name').val(),\r\n        Amount: parseInt($('#Amount').val()),\r\n        Brand: $('#Brand').val(),\r\n        Value: parseFloat($('#Value').val()),\r\n    };\r\n    $.ajax({\r\n        url: \"\/api\/products\",\r\n        data: JSON.stringify(productObj),\r\n        type: \"PUT\",\r\n        contentType: \"application\/json;charset=utf-8\",\r\n        dataType: \"json\",\r\n        success: function () {\r\n            LoadProducts();\r\n            $('#productModal').modal('hide');\r\n            clearStuff();\r\n        },\r\n        error: function (message) {\r\n            console.log(message.responseText);\r\n        }\r\n    });\r\n}\r\nfunction DeleteProduct(id) {\r\n    if (confirm(\"Are you sure?\")) {\r\n        $.ajax({\r\n            url: \"\/api\/products\/\" + id,\r\n            type: \"DELETE\",\r\n            contentType: \"application\/json;charset=UTF-8\",\r\n            dataType: \"json\",\r\n            success: function () {\r\n                LoadProducts();\r\n            },\r\n            error: function (message) {\r\n                console.log(message.responseText);\r\n            }\r\n        });\r\n    }\r\n}\r\n\/** Utility functions **\/\r\nfunction clearStuff() {\r\n    $('form').trigger(\"reset\");\r\n    $('#btnUpdateProduct').hide();\r\n    $('#productModal h4').text('Add Product');\r\n    $('#btnAddProduct').show();\r\n}\r\nfunction validateForm() {\r\n    var isValid = true;\r\n    if ($('#Name').val().trim()      == \"\") {\r\n        $('#Name').css('border-color', '#c00');\r\n        isValid = false;\r\n    }\r\n    else {\r\n        $('#Name').css('border-color', 'grey');\r\n    }\r\n    if ($('#Amount').val().trim() == \"\") {\r\n        $('#Amount').css('border-color', '#c00');\r\n        isValid = false;\r\n    }\r\n    else {\r\n        $('#Amount').css('border-color', 'grey');\r\n    }\r\n    if ($('#Brand').val().trim() == \"\") {\r\n        $('#Brand').css('border-color', '#c00');\r\n        isValid = false;\r\n    }\r\n    else {\r\n        $('#Brand').css('border-color', 'grey');\r\n    }\r\n    if ($('#Value').val().trim() == \"\") {\r\n        $('#Value').css('border-color', '#c00');\r\n        isValid = false;\r\n    }\r\n    else {\r\n        $('#Value').css('border-color', 'grey');\r\n    }\r\n    return isValid;\r\n}\r\nLoadProducts();<\/pre>\n<p>Note that for each one of the HTTP requests for the CRUD operations, you also have a different JavaScript function to handle it. It helps to simplify the separation. Here, you\u2019re using jQuery <a href=\"https:\/\/api.jquery.com\/jquery.ajax\/\">ajax<\/a> function to help with the remote requests and responses to the Products API.<\/p>\n<p>This function takes a bunch of inputs regarding the URL, content type, headers, params and two functions for success and error handling in the requesting\/responding process.<\/p>\n<p>The first function, <code>LoadProducts()<\/code>, makes an HTTP GET over the respective API endpoint to retrieve the list of products from the gRPC server project. If the request is successful, the results must come as a parameter, which will be iterated to get each one of the values and, finally, display them in the table\u2019s <code>tbody<\/code>. Due to Ajax\u2019s async nature, you may notice that the list takes a bit to appear. In case any error occurs, it\u2019ll be displayed in the console.<\/p>\n<p>Both adding and updating functions make use of the utility function <code>validateForm()<\/code>. This auxiliary function is just to make sure the data is correct and filled, before sending to the API. You can change it to your own code or lib.<\/p>\n<p>For the adding, updating and deleting actions, you need to re-render the list of products by calling the loading function again. And, then, hide the modal.<\/p>\n<p>When loading the table\u2019s elements, you also need to create the links for editing and deleting calls. Note that the first triggers the function <code>SetUpEditModal()<\/code> which, in turn, will seek for that specific product by the provided id and fill each of the modal form\u2019s inputs with the returned values. That\u2019s important since you want to show each input prefilled with the respective value before updating them.<\/p>\n<p>The <code>UpdateProduct()<\/code> function is the one that, in fact, summarize the updated input values and send them to the server. Here, in case of success, and after hiding the modal and refreshing the product\u2019s table, you also need to rename the title of the modal (remember, it\u2019s one single modal for both operations). The <code>clearStuff()<\/code> function exists to reset the form and show\/hide proper buttons and texts.<\/p>\n<p>At the end of the JS file, you also make sure to call the <code>LoadProducts()<\/code> function, since every time the page loads, you need the table filled.<\/p>\n<p>Before going to the tests, check two things. First, make sure to have the following snippet just after the jQuery and Bootstrap JavaScript imports (not before), located in <em>Views\/Shared\/_Layout.cshtml<\/em>:<\/p>\n<pre class=\"lang:c# theme:vs2012\">&lt;script <strong>src<\/strong>=\"~\/js\/site.js\" <strong>asp-append-version<\/strong>=\"true\"&gt;&lt;\/script&gt;     <\/pre>\n<p>Second, open the <em>site.css<\/em> file under <em>wwwroot\/css<\/em> folder and add the following class:<\/p>\n<pre class=\"lang:c# theme:vs2012\">.redgate {\r\n    background-color: #c00;\r\n    color: #fff;\r\n}<\/pre>\n<p>It\u2019s important to add the background color effect related to the RedGate website.<\/p>\n<p>Test it now. With the server project up and running, start your web application (<em>Ctrl + F5<\/em>). When the browser window open, you should see something like Figure 5.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-87077\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/04\/word-image-77.png\" alt=\"Integrating ASP.NET Core with gRPC\" width=\"1084\" height=\"796\" \/><\/p>\n<p><strong>Figure 5. Web client application running in the browser.<\/strong><\/p>\n<h2>Integrating ASP.NET Core with gRPC<\/h2>\n<p>This is it. Go ahead and play with the other operations. Feel free to customize the way you prefer the screen and the overall architecture.<\/p>\n<p>Please, also refer to the <a href=\"https:\/\/grpc.io\/docs\/quickstart\/csharp\/\">official docs<\/a> for more on what (and how) you can do with gRPC in .NET. It\u2019s the most complete and trustworthy source for your studies. Regarding tests and performance, please also refer to their <a href=\"https:\/\/grpc.io\/docs\/guides\/benchmarking\/\">Benchmarking page<\/a>.<\/p>\n<p>As a challenge, you can try to upgrade your implementation to integrate it with the <a href=\"https:\/\/grpc.io\/docs\/quickstart\/web\/\">Web version<\/a> of gRPC, along with the current CRUD page. Good luck with the studies!<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article, Diogo Souza explains integrating ASP.NET Core with gRPC which is an improvement from Google over the classic remote procedure calls used when a program must communicate between resources on a network.&hellip;<\/p>\n","protected":false},"author":320401,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[95509],"coauthors":[60461],"class_list":["post-87072","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-standardize"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/87072","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/320401"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=87072"}],"version-history":[{"count":4,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/87072\/revisions"}],"predecessor-version":[{"id":89539,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/87072\/revisions\/89539"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=87072"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=87072"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=87072"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=87072"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}