© Peter Himschoot 2020
P. HimschootMicrosoft Blazorhttps://doi.org/10.1007/978-1-4842-5928-3_5

5. Data Storage and Microservices

Peter Himschoot1 
(1)
Melle, Belgium
 

In general, client-side browser applications need to store some of their data. In some cases, such as games, the application can store its data in the browser itself, using browser local storage. But in most cases, storage will happen on the server, which has access to database engines such as SQL Server. In this chapter, you will learn the basics of storing data using Entity Framework Core and exposing that data using REST and microservices built on top of ASP.NET Core.

What Is REST?

Storing data on the Web is ubiquitous. But how can applications communicate with one another? Representational State Transfer (REST) is a protocol built on top of the HTTP protocol for invoking functionality on servers, such as retrieving and storing data from/in a database.

Understanding HTTP

Before talking about REST, you should have a good understanding of the Hypertext Transfer Protocol , better known as HTTP. HTTP was created by Tim Berners-Lee at CERN in 1989. CERN is a center for elementary physics research, and what do researchers do when they have completed their research? They publish papers with their research findings. Before the Internet, publishing a paper was done literally on paper (hence the name), and it took a lot of time between writing the paper and getting it published in a research magazine. Instead, Tim Berners-Lee devised a way to put papers on a server and allow users to read these papers using a browser.

Also, scientific papers contain a lot of references, and when you want to read a paper like this, it helps to be able to access the referenced papers. The Internet facilitates reading papers through the use of Hypertext Markup Language (HTML). Hypertext is an electronic document format that can contain links to other documents. You simply click the link to read the other paper and you can go back to the first paper simply by clicking the back button in your browser.

Universal Resource Identifiers and Methods

Browsers are applications that know how to talk HTTP, and the first thing you do after opening a browser is you type in a Universal Resource Identifier (URI). A URI allows a browser to talk to a server, but more is needed. As the name suggests, a URI identifies a resource universally, but you also need to use a method to instruct the server to do something with the URI. The most common method is GET. As Figure 5-1 shows, when you type in a URI in the browser, it will do a GET on the server.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig1_HTML.jpg
Figure 5-1

The browser uses the GET method to retrieve a document

Each time you click a hyperlink in the HTML document, the browser repeats this process with another URI.

But there are other methods. If you want to publish a new paper, you can use the POST method to send the paper to the server, supplying it with a URI. In this case, the server will store the paper at the requested URI. If you want to make a change to your paper, for example, to correct a spelling mistake, you can use the PUT method. Now the server will overwrite the URI contents. And finally, you can delete the paper using the DELETE method and its URI.

HTTP Status Codes

What happens when you ask a server about something it doesn’t have? What should the server return? Servers not only return HTML, but they also return a status code about the result. When the server can process the request successfully, it will in general return status code 200 (other successful status codes exist). When the server can’t find the resource, it will return a status code 404. Status code 404 simply means not found. The client will receive this status code and can react appropriately. When the browser receives a status code 200, it displays the HTML; when it receives a 404, it displays a not found screen, and so on.

A full list of all HTTP status codes can be found at https://en.wikipedia.org/wiki/List_of_HTTP_status_codes.

Invoking Server Functionality Using REST

Think about these methods we just talked about. With POST, you can CREATE something on a server; with GET, you can READ it back; with PUT, you can UPDATE something on the server, and with DELETE, you can DELETE things on the server. They are also known as CRUD operations (CREATE-READ-UPDATE-DELETE). Roy Fielding , the inventor of REST, realized that using the HTTP protocol you can also use HTTP to work with data stored in a database. For example, if you use the GET method with a URI http://someserver/categories, the server can execute some code to retrieve data from the categories relational table and return it. Of course, the server would use a format more appropriate for transferring data, such as XML or JSON. Because there are many different formats for data, the server also needs a way to convey which format it is sending. (At the beginning of the Web, only HTML was used as the format.) This is done through HTTP headers.

HTTP Headers

HTTP headers are instructions exchanged between the client and the server. Headers are key-value pairs, where the client and server agree on the key. Many standard HTTP headers exist. For example, a server can use the Content-Type header to tell the client to expect a specific format. Another header is the Accept header , which is sent by the client to the server to politely ask the server to send the content in that format; this is also known as content negotiation . Currently, the most popular format is JavaScript Object Notation (JSON). And this is the exchange format you will use with Blazor.

JavaScript Object Notation

JSON is a compact format for transferring data. Look at the example in Listing 5-1.
{ "book" : {
  "title" : "Blazor Revealed",
  "chapters" : [ "Your first Blazor project", "Data Binding"]
  }
}
Listing 5-1

An example of JSON

This JSON format describes a book, an object in memory. Objects are denoted using curly braces. Inside the book are two properties; each property uses a key : value notation. The book’s title is "Blazor Revealed". Note that the property name is also transferred as a string. And finally, the “chapters” property is an array of strings, where you use square brackets to indicate an array.

The JSON format is used for transferring data between two machines, but today is also heavily used for configuring tools such as ASP.NET Core. JSON today is way more popular on the Web than XML, probably because of its simplicity.

Some Examples of REST Calls

You need a list of pizzas from a server, and the server exposes the pizzas at URI http://someserver/pizza. To get a list of pizzas, you use the GET method, and you use the Accept header with value application/json to request the JSON format. Look at Figure 5-2 for this example.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig2_HTML.jpg
Figure 5-2

Using REST to retrieve a list of pizzas

Maybe your client wants to display the details of a pizza with id number 5. In this case, it can append the id to the URI and perform a GET. Should the server not have any pizza with that id, it can return a status code 404, as illustrated in Figure 5-3.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig3_HTML.jpg
Figure 5-3

Using REST to retrieve a specific pizza through its unique id

As the last example, let’s send some data from the client to the server. Imagine that the customer has filled in all the details for the order and clicks the Order button. You then send the order as JSON to the server using the POST method (remember POST means insert). The server can then process the order in any way it likes; for instance, it can insert the order into its database and return a 201: Created status code, as in Figure 5-4. REST recommends returning a status code 201 with the Location header set to the URI for the newly created resource.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig4_HTML.jpg
Figure 5-4

POSTing an order to the server

Building a Simple Microservice Using ASP.NET Core

So, how do you build a REST service? Your Blazor project uses ASP.NET Core for hosting the Blazor client, and adding a service to your project is easy. But first, let’s do a little intro to microservices.

Services and Single Responsibility

A service is a piece of software that listens for requests; when it receives a request, the service handles the request and returns with a response. In real life, you also encounter services and they are very similar. Consider a bank. You step into a bank and you give the teller your account number and some ID and request $100. The teller will check your account; if you have enough money in your account, the teller will deduct the money and give you the cash. Should your account be too low, the teller will refuse. In both cases, you got a response.

Services should also adhere to the principle of single responsibility . They should do one thing very well, and that’s it. For example, the pizza service will allow clients to retrieve pizzas, add, update, and delete pizzas. That’s it. A single responsibility, in this case, PIZZAS.

You can have other services too, each with their own responsibility. Services that take care of one thing are known as microservices .

The Pizza Service

Open the PizzaPlace solution you worked on in previous chapters. In this chapter, you will focus on the PizzaPlace.Server project, shown in Figure 5-5.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig5_HTML.jpg
Figure 5-5

The PizzaPlace.Server project

The only role this project currently has is to host your Blazor client application, but now you will enhance this role by adding some microservices.

Open Startup.cs and look at the Configure method , as in Listing 5-2.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
    app.UseWebAssemblyDebugging();
  }
  app.UseStaticFiles();
  app.UseBlazorFrameworkFiles();
  app.UseRouting();
  app.UseEndpoints(endpoints =>
  {
    endpoints.MapRazorPages();
    endpoints.MapControllers();
    endpoints.MapFallbackToFile("index.html");
  });
}
Listing 5-2

The Startup class’s Configure method

The last line with the endpoints.MapFallbackToFile("index.html") method takes care of your Blazor client project. But right before it, you see the endpoints.MapControllers() method that is used for hosting your services.

How the MapControllers method works is not the topic of this book, but I will cover what you need to know. If you want to learn more about ASP.NET Core, there are many good books about this topic, such as Pro ASP.NET Core MVC by Adam Freeman.

Next in line is the Controllers folder. In Figure 5-5, this folder is empty, and the idea is that you put your service classes here. In ASP.NET, service classes are known as controllers. If you are using Visual Studio, right-click this folder and select Add ➤ Controller. Select API Controller - Empty from Figure 5-6 and click Add.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig6_HTML.jpg
Figure 5-6

Adding a new controller

Type PizzasController as in Figure 5-7 and click Add again.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig7_HTML.jpg
Figure 5-7

Naming the controller

If you are using Code, simply right-click the Controllers folder and select Add File. Name it PizzasController.cs.

This will add a new class called PizzasController, inheriting from ControllerBase, which you can see in Listing 5-3. Please note that the Route attribute indicates that the URI you should use is api/pizzas. The [controller] part of the route is a placeholder for the name of the controller, but without the Controller part.
using Microsoft.AspNetCore.Mvc;
namespace PizzaPlace.Server.Controllers
{
  [Route("api/[controller]")]
  [ApiController]
  public class PizzasController : ControllerBase
  {
  }
}
Listing 5-3

The empty PizzasController

Let’s add a method to retrieve a list of pizzas. For the moment, you will hard-code the list, but in the next section, you will retrieve it from a database. Modify the PizzasController as shown in Listing 5-4.
using Microsoft.AspNetCore.Mvc;
using PizzaPlace.Shared;
using System.Collections.Generic;
using System.Linq;
namespace PizzaPlace.Server.Controllers
{
  [ApiController]
  public class PizzasController : ControllerBase
  {
    private static readonly List<Pizza> pizzas = new List<Pizza>
        {
          new Pizza(1, "Pepperoni", 8.99M, Spiciness.Spicy ),
          new Pizza(2, "Margarita", 7.99M, Spiciness.None ),
          new Pizza(3, "Diabolo", 9.99M, Spiciness.Hot )
        };
    [HttpGet("pizzas")]
    public IQueryable<Pizza> GetPizzas()
      => pizzas.AsQueryable();
  }
}
Listing 5-4

Adding a method to the PizzaController to retrieve a list of pizzas

Let’s walk through this implementation. First, you declare a hard-coded static list of pizzas. Next is the GetPizzas method, which has an attribute of HttpGet("pizzas"). This attribute says that when you perform a GET on the server with the pizzas URI, the server should call the GetPizzas method.

The GetPizzas method returns an IQueryable<Pizza>, and ASP.NET Core will send this result back to the client with the requested format. The IQueryable<Pizza> interface is used in .NET to represent data that can be queried, such as database data, and is returned by LINQ queries.

Note that the GetPizzas method contains nothing about HOW the data will be transferred to the client. This is all taken care of for you by ASP.NET Core! By default, your implementation in ASP.NET Core will use JSON, which is what you want. ASP.NET Core allows you to pick other formats, including your custom format. This is not the scope of this book.

Time to see if it works. First, ensure that the PizzaPlace.Server project is the startup project. Right-click the PizzaPlace.Server project and select Set as Startup Project from the drop-down menu. The PizzaPlace.Server project should be shown as bold, as in Figure 5-5.

Now run your project and wait for the browser to open because you will perform a GET; you can use the browser, but for other methods, you will later use a nice tool called Postman.

Change the URI in the browser to http://localhost:xxxx/pizzas where xxxx is the original port number in your browser (the port number gets selected by Visual Studio and will be different than mine). You should see the result shown in Figure 5-8.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig8_HTML.jpg
Figure 5-8

The results of getting a list of pizzas from the pizza service

A JSON-encoded list of pizzas! It works!

Now you are ready to retrieve the data from a real database using Entity Framework Core.

What Is Entity Framework Core?

Entity Framework Core is the framework Microsoft recommends for working with databases. It allows you to write classes as normal C# classes and then store and retrieve .NET objects from a database without having to be an SQL expert. It will take care of this for you. This is also known as persistence ignorance , where your code does not need to know how and where data gets stored!

Using the Code First Approach

But of course, you need to explain to Entity Framework Core what kind of data you want to store. Entity Framework Core uses a technique called code first , where you write code to describe the data and how it should be stored in the database. Then, you can use this to generate the database, the tables, and the constraints. If you want to make changes to the database, you can update the database schema with code first migrations .

If you already have a database, you can also generate the code from the database, also known as EF Database First, but this is not the target of this book.

In the code first approach, you describe the classes (also known as entities) that will map to database tables. You already have the Pizza class (which you can find in the PizzaPlace.Shared project) to describe the Pizza table in the database. But you need to do more.

In this part, you will be using SQL Server. If you installed Visual Studio on your Windows machine, SQL Server was installed too. If you don’t have SQL Server on your machine, you can install a free version of SQL Server, or use a SQL Server instance in the cloud, for example, SQL Server on Azure (https://azure.microsoft.com/en-us/get-started/). You can even install SQL Server on OSX! There are some nice articles on the Web that explain how.

You need to add Entity Framework Core to the PizzaPlace.Server project. If you are using Visual Studio, right-click the server project and select Manage NuGet Packages…, as shown in Figure 5-9.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig9_HTML.jpg
Figure 5-9

Adding NuGet packages to your project

The NuGet window will open in Visual Studio. NuGet is a very practical way for installing dependencies such as Entity Framework Core to your project. It will not only install the Microsoft.EntityFrameworkCore.SqlServer library but also all its dependencies.

Select the Browse tab and type Microsoft.EntityFrameworkCore.SqlServer in the search box. You should see this library as the top search result. Select it, then select the Latest stable 3.1.1 version from the Version drop-down, and click the Install button, as shown in Figure 5-10.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig10_HTML.jpg
Figure 5-10

Adding Entity Framework Core using NuGet

By the time you read this book, Microsoft might have deployed a more recent version, so although Figure 5-10 shows version 3.1.1, you should select the latest stable version.

With Code, you open the command prompt and type in the following command after changing the current directory of your PizzaPlace.Server project:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
With this dependency installed, you are ready to make some code changes. Entity Framework Core requires that entity classes have a default constructor and that properties are read-write. Update the Pizza class by adding a default constructor and adding setters for the properties, as shown in Listing 5-5.
using System;
namespace PizzaPlace.Shared
{
  public enum Spiciness
  {
    None,
    Spicy,
    Hot
  }
  public class Pizza
  {
    public Pizza() { }
    public Pizza(int id, string name, decimal price,
                 Spiciness spiciness)
    {
      Id = id;
      Name = name
        ?? throw new ArgumentNullException(nameof(name),
           "A pizza needs a name!");
      Price = price;
      Spiciness = spiciness;
    }
    public int Id { get; set; }
    public string Name { get; set;  }
    public decimal Price { get; set; }
    public Spiciness Spiciness { get; set; }
  }
}
Listing 5-5

Modifying the Pizza class for Entity Framework Core

Add a new class called PizzaPlaceDbContext to the PizzaPlace.Server project, as shown in Listing 5-6. This class represents the database, and you do need to give a couple of hints about how you want your data to be stored in SQL Server (or some other database engine; this uses the same code).
using Microsoft.EntityFrameworkCore;
using PizzaPlace.Shared;
namespace PizzaPlace.Server
{
  public class PizzaPlaceDbContext : DbContext
  {
    public PizzaPlaceDbContext(DbContextOptions<PizzaPlaceDbContext> options)
      : base(options) { }
    public DbSet<Pizza> Pizzas { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
      base.OnModelCreating(modelBuilder);
      var pizzaEntity = modelBuilder.Entity<Pizza>();
      pizzaEntity.HasKey(pizza => pizza.Id);
      pizzaEntity.Property(pizza => pizza.Price)
        .HasColumnType("money");
    }
  }
}
Listing 5-6

The PizzaPlaceDbContext class

First, you need to create a constructor for the PizzaPlaceDbContext class taking an DbContextOptions<PizzaPlaceDbContext> argument. This is used to pass the connection to the server, which you will do later in this section.

Next, you add a table to the database to represent your pizzas using a public property of type DbSet<Pizza>. DbSet<T> is the collection class used by Entity Framework Core, but you can think of it as a List<T>. Entity Framework Core will use the DbSet<T> to map this collection to a table, in this case, the Pizzas table.

Finally, you override the OnModelCreating method, which takes a modelBuilder argument. In the OnModelCreating method, you can describe how each DbSet<T> should be mapped to the database; for example, you can tell it which table to use, how each column should be called, which type to use, and so on. This modelBuilder has a bunch of methods that allow you to describe how the classes should be mapped to your database. In this case, you tell the modelBuilder that the Pizza table should have a primary key, the Id property of the Pizza class. You also need to tell how the Pizza.Price property should be mapped to SQL Server. You will use the SQL Server MONEY type for that. For the moment, this is enough for your current implementation.

Preparing Your Project for Code First Migrations

Now you are ready to tell the PizzaPlaze.Server project to use SQL Server as the database. You do this with dependency injection. In ASP.NET Core, you configure dependency injection in the Startup class’s ConfigureServices method. Let’s have a look at this method which is shown in Listing 5-7.
public void ConfigureServices(IServiceCollection services)
{
  services.AddControllersWithViews();
  services.AddRazorPages(); 
}
Listing 5-7

The Startup.ConfigureServices method

Remember IServiceCollection from the chapter on dependency injection? Here dependencies for ASP.NET Core are added, such as dependencies for Mvc and Razor pages, which are required for your service.

Start by adding a constructor to the Startup class as in Listing 5-8.
...
using Microsoft.Extensions.Configuration;
namespace PizzaPlace.Server
{
  public class Startup
  {
    public Startup(IConfiguration configuration)
      => this.Configuration = configuration;
    public IConfiguration Configuration { get; }
Listing 5-8

The Startup class’s constructor

You need this constructor to have access to the project's configuration file. The configuration will contain the connection string for the database to talk to.

In the ConfigureServices, you need to add any additional dependencies your implementation requires. Add the following code from Listing 5-9 at the end of the method.
services.AddDbContext<PizzaPlaceDbContext>(options
  => options.UseSqlServer(Configuration.GetConnectionString("PizzaDb")));
Listing 5-9

Adding Entity Framework dependencies

This single statement tells ASP.NET Core that you will be using the PizzaPlaceDbContext and that you will be storing it in SQL Server. This code also looks up the connection string for the database in configuration, which you still need to add.

Right-click the PizzaPlace.Server project and select Add ➤ New Item. Type JSON in the search box and select App Settings File, as shown in Figure 5-11. Keep the default name of appsettings.json and click Add.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig11_HTML.jpg
Figure 5-11

Adding the application configuration file

With Code, simply add a new file called appsettings.json. Double-click the new appsettings.json file to open it. ASP.NET Core uses a JSON file for configuration, and you need to add a connection string to the database. A database connection string tells your code where to find the database server, which database to use, and which credentials should be used to log in. Visual Studio added a configuration file such as in Listing 5-10. This connection string uses the (localdb)\MSSQLLocalDB server, which is the server installed with Visual Studio. The only things you need to do are to set the database name by replacing _CHANGE_ME into a more suitable name for your database and to change the name of the connection. Of course, if you are using another database server, you will also have to change the server name too. Or read on to find out how to get the connection string with Visual Studio.
{
  "ConnectionStrings": {
    "PizzaDb": "Server=(localdb)\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}
Listing 5-10

The appsettings.json configuration file

Finding Your Database Server’s Connection String

If you are not sure which connection string to use, you can find the connection string in Visual Studio by selecting View ➤ SQL Server Object Explorer.

You can connect to a database by clicking the server icon with the little green + sign, shown in Figure 5-12.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig12_HTML.jpg
Figure 5-12

SQL Server Object Explorer

You can look for available database servers by expanding the Local, Network, or Azure as in Figure 5-13. I recommend that you try to find the MSSQLLocalDB database server. If you use another database server, you might need to change how to log in to your database. When you’re ready, click Connect.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig13_HTML.jpg
Figure 5-13

Finding the connection string for a database

Next, expand SQL Server from Figure 5-13 and select your server. Right-click it and select Properties. Now copy the connection string from the properties window and change the database name to PizzaDb.

Creating Your Code First Migration

You are almost ready to generate the database from the code. Start by adding the Microsoft.EntityFrameworkCore.Design NuGet package to the PizzaPlace.Server project.

Now you need to create a migration. A migration is a C# class that contains the changes that need to be made to the database to bring it up (or down) to the schema your application needs. This is done through a tool.

Start by selecting from the Visual Studio menu View ➤ Other Windows ➤ Package Manager Console, which you can see in Figure 5-14. Or use the command line (cmd.exe) if you prefer.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig14_HTML.jpg
Figure 5-14

The Package Manager Console

Make sure that the default project is set to PizzaPlace.Server. This will make your commands target the selected project.

If you are using Code, use the integrated terminal or open a command prompt.

You must run the next command in the PizzaPlace.Server directory, so make sure you are in the correct directory. Optionally, type the following command to change the current directory to the PizzaPlace.Server project’s directory:
cd PizzaPlace
cd Server

You might need to install a command-line tool as well. Run the following command to install the migration tool. You only need to install this tool once.

dotnet tool install --global dotnet-ef

Now execute the following command to create the migration:
dotnet-ef migrations add CreatingPizzaDb
Here you use the dotnet-ef tool to add a new migration called CreatingPizzaDb. You should see the following output (please ignore any differences in versions being shown):
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 3.1.1 initialized 'PizzaPlaceDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
Done. To undo this action, use 'ef migrations remove'

Should you get an error or warnings, please review the code for the Pizza and the PizzaPlaceDbContext classes and try again.

This tool created a new Migrations folder in the PizzaPlace.Server project with two files similar to Figure 5-15 but with a different timestamp.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig15_HTML.jpg
Figure 5-15

The result of adding the first migration

Open the CreatingPizzaDb.cs file from Listing 5-11 and look at what the tool did.
using Microsoft.EntityFrameworkCore.Migrations;
namespace PizzaPlace.Server.Migrations
{
  public partial class CreatingPizzaDb : Migration
  {
    protected override void Up(MigrationBuilder migrationBuilder)
    {
      migrationBuilder.CreateTable(
          name: "Pizzas",
          columns: table => new
          {
            Id = table.Column<int>(nullable: false)
                  .Annotation("SqlServer:Identity", "1, 1"),
            Name = table.Column<string>(nullable: true),
            Price = table.Column<decimal>(type: "money", nullable: false),
            Spiciness = table.Column<int>(nullable: false)
          },
          constraints: table =>
          {
            table.PrimaryKey("PK_Pizzas", x => x.Id);
          });
    }
    protected override void Down(MigrationBuilder migrationBuilder)
    {
      migrationBuilder.DropTable(
          name: "Pizzas");
    }
  }
}
Listing 5-11

The CreatingPizzaDb.cs file

A migration class has two methods: Up and Down. The Up method will upgrade the database schema. In this case, it will create a new table called Pizzas with Id, Name, Price, and Spiciness columns.

The Down method downgrades the database schema, in this case, by dropping the column.

Generating the Database

Now you are ready to generate the database from your migration. With Visual Studio, go back to the Command Line or Package Manager Console window (View ➤ Other Windows ➤ Package Manager Console), or with Code, open the integrated terminal (View ➤ Terminal) and type the following command:
dotnet-ef database update --verbose
Because you asked the tool to be verbose, this will generate a lot of output, among which you will find the DDL statements executed, such as in Listing 5-12.
CREATE TABLE [Pizzas] (
          [Id] int NOT NULL IDENTITY,
          [Name] nvarchar(max) NULL,
          [Price] money NOT NULL,
          [Spicyness] int NOT NULL,
          CONSTRAINT [PK_Pizzas] PRIMARY KEY ([Id])
);
Listing 5-12

An extract from the database generation tool’s output

This just created the database for you!

Let’s have a look at the database. From Visual Studio, open View ➤ SQL Server Object Explorer and expand the tree for the PizzaDb database as in Figure 5-16 (you might need to refresh the database: right-click Databases and select Refresh).
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig16_HTML.jpg
Figure 5-16

SQL Server Object Explorer showing the PizzaDb database

If you don’t have Visual Studio, you can download Azure Data Studio from www.microsoft.com/en-us/sql-server/developer-tools. After installation ends, Azure Data Studio will start. Enter your server name and select PizzaDb from the drop-down list, as shown in Figure 5-17.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig17_HTML.jpg
Figure 5-17

Connection with Azure Data Studio

Enhancing the Pizza Microservice

Let’s add some functionality to the Pizza microservice so it uses the database instead of hard-coded data and add a method to insert a pizza in your database.

Open the PizzaController class, which sits in the Controllers folder of the PizzaPlace.Server project. Start by adding a constructor that takes the PizzaPlaceDbContext as an argument, as in Listing 5-13.
using Microsoft.AspNetCore.Mvc;
using PizzaPlace.Shared;
using System.Linq;
namespace PizzaPlace.Server.Controllers
{
  [ApiController]
  public class PizzasController : ControllerBase
  {
    //private static readonly List<Pizza> pizzas = new List<Pizza>
    //    {
    //      new Pizza(1, "Pepperoni", 8.99M, Spiciness.Spicy ),
    //      new Pizza(2, "Margarita", 7.99M, Spiciness.None ),
    //      new Pizza(3, "Diabolo", 9.99M, Spiciness.Hot )
    //    };
    private readonly PizzaPlaceDbContext db;
    public PizzasController(PizzaPlaceDbContext db)
      => this.db = db;
  }
}
Listing 5-13

Injecting a PizzaPlaceDbContext instance into the controller

To talk to the database, the PizzasController needs a PizzaPlaceDbContext instance, and as you learned in the chapter on dependency injection, you can use a constructor to do this. The constructor only needs to save the reference in a local field (for now).

You don’t need the hard-coded list of pizzas, so remove the static field, and update the GetPizza method to use the PizzaPlaceDbContext instead, as in Listing 5-14. To get all the pizzas, you can simply use the Pizzas property of the PizzaPlaceDbContext. The Entity Framework will access the database when it accesses the Pizzas property.
[HttpGet("pizzas")]
public IQueryable<Pizza> GetPizzas()
  => this.db.Pizzas;
Listing 5-14

Retrieving the pizzas from the database

Now let’s add a method to insert a new pizza in the database. Add the InsertPizza method from Listing 5-15 to the PizzasController class. This method will receive a pizza instance from the client as part of the POST request body, so you add the HttpPost attribute with the URI that you should post to. The pizza object will be posted in the request body, and this is why the InsertPizza method’s pizza argument has the FromBody attribute to tell ASP.NET MVC Core to convert the body to a pizza instance. The method adds the pizza to the PizzaPlaceDbContext Pizzas table and then saves it to the database. The InsertPizza method then returns a 201: Created status code with the URI of the pizza as the response. There are many possible HTTP status codes that you could return from this method. But the most common of them have special helper methods that make it easy to return a certain status code, for instance, Ok(), NotFound(). In this case, you return a 201: Created status code. You will examine this response with Postman in the next part of this chapter.
[HttpPost("pizzas")]
public IActionResult InsertPizza([FromBody] Pizza pizza)
{
  db.Pizzas.Add(pizza);
  db.SaveChanges();
  return Created($"pizzas/{pizza.Id}", pizza);
}
Listing 5-15

The InsertPizza method

This is an introduction to REST services. Building real services with all the different approaches and best practices can take up a whole book. The idea of this chapter is to get you up and running.

Testing Your Microservice Using Postman

So now, you have your first microservice. But how do you test it? Previously, you used the browser to test the GetPizzas method, which uses the GET method. For other methods such as POST, PUT, and DELETE, you need a better tool. Here you will use Postman, which is a tool specifically for testing REST services.

Installing Postman

Open your favorite browser and go to www.getpostman.com. Download the application (click the Download the App button from Figure 5-18 and choose your platform) and install it.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig18_HTML.jpg
Figure 5-18

The Postman web page

By the time you read this book, the installation procedure may have changed a bit, so please follow the instructions from the installer.

After it has installed, run Postman.

Making REST Calls with Postman

Postman will open, and it will ask you what you want to do. Select Request, as shown in Figure 5-19.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig19_HTML.jpg
Figure 5-19

Select Request to get started with Postman

Then it will ask you where to save the request, so pick a name and a folder, as shown in Figure 5-20.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig20_HTML.jpg
Figure 5-20

Saving the request

Making a GET Request

Now run the PizzaPlace solution and copy the URL from the browser. Paste it in Postman’s “Enter the Request URL” field and append /pizzas as in Figure 5-21. Don’t forget that you most likely will have a different port number!
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig21_HTML.jpg
Figure 5-21

Making a GET request with Postman

Before you click Send, let’s add the Accept header. Click the Headers tab and enter Accept as the key and application/json as the value. Please refer to Figure 5-22 for reference.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig22_HTML.jpg
Figure 5-22

Adding headers to the request in Postman

Now you can click Send. You should receive an empty list as in Figure 5-23 (which is normal because you haven’t added any rows to the pizza table yet).
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig23_HTML.jpg
Figure 5-23

Receiving an empty list of pizzas from the server

Inserting Pizzas with POST

Let’s add a couple of pizzas to the database. At the top of Postman, you will find a tab with a plus sign. Click it to add another tab. Select POST as the method and copy the URI from the previous tab, as shown in Figure 5-24.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig24_HTML.jpg
Figure 5-24

Starting with the POST request

Now select the Headers section and add a new header with key Content-Type and value application/json like in Figure 5-25.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig25_HTML.jpg
Figure 5-25

Adding the Content-Type header for the POST request

Now select the Body section, click the raw format radio button, and enter a pizza object using JSON. Please refer to Figure 5-26. Note that this raw string contains the pizza’s properties serialized as JSON and that you don’t need to send the Id property because the server will generate the id when it gets inserted into the database.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig26_HTML.jpg
Figure 5-26

Entering a pizza using JSON

Ensure your PizzaPlace application is still running.

Click the Send button. If all is well, you should receive a positive 201: Created response. In the response area of Postman, select the Headers tab as in Figure 5-27. Look for the Location header. It will show the new URI given to this pizza. This Location header is returned by the Created method you called as the last line of Listing 5-15.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig27_HTML.jpg
Figure 5-27

The POST response in Postman

Click the first tab where you created the GET request and click Send again. Now you should have a list of pizzas (a list of one). Try creating a couple of other pizzas. Figure 5-28 is my result after adding three pizzas.
../images/469993_2_En_5_Chapter/469993_2_En_5_Fig28_HTML.jpg
Figure 5-28

A list of pizzas stored in the database

Summary

In this chapter, you had a look at how to store data on the server using Entity Framework Core and how to expose that data using REST and microservices. You added a pizza service to the PizzaPlace application and then went on testing it with Postman.

In the next chapter, you will learn how to talk to your service(s) from Blazor.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.79.70