Friday, May 31, 2019

Blazor client-side app with CRUD operations against a Web API endpoint

A companion video for this article is: https://www.youtube.com/watch?v=wkSR3eo4Tek

Source code for this application can be found at:
https://github.com/medhatelmasry/BlazingSchool

Overview

What is Blazor?
Blazor is a framework for developing interactive client-side web applications using C# instead of JavaScript.

What are we going to do in this Tutorial?In this tutorial I will show you how to build a Blazor application with the following structure:
1) The class library will have model definitions that are shared between the Web API and Blazor Visual Studio projects. The model that we will create in this tutorial is a simple C# Student model that looks like this:

public class Student {
  public string StudentId { get; set; }
  [Required]
  public string FirstName { get; set; }
  [Required]
  public string LastName { get; set; }
  [Required]
  public string School { get; set; }
}

2) The ASP.NET Core Web API app will provide the REST endpoints for the students service that the Blazor client-side application will consume. It will use the GET, POST, PUT and DELETE methods to carry out CRUD operations with the API service. Entity Framework will be used to save data in SQL Server using the "Code First" approach.

3) The Client-side Blazor app will provide the user interface for processing student data.

Perquisites

This tutorial was written while Blazor was in preview mode. I used the following site for setting up my environment:

This is how I setup my development environment:

- .NET Core 3.0 Preview SDK installed from https://dotnet.microsoft.com/download/dotnet-core/3.0

- Install Blazor templates by running the following command in a terminal window:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview5-19227-01

- Visual Studio 2019 preview version installed from: https://visualstudio.com/preview

- Latest Blazor extension for Visual Studio 2019 installed from https://go.microsoft.com/fwlink/?linkid=870389.

Creating the class library

Create a new project in Visual Studio 2019.

Choose "Class Library (.NET Standard)" then click Next:

Set these values on the next dialog:

Project name: SchoolLibrary
Solution name: School

Click on Create.

We need to install a package to the class library project. From within a terminal window at the main SchoolLibrary folder, run the following command:

dotnet add package System.ComponentModel.Annotations

Delete Class1.cs, then create a new Student.cs class file and add to it the following code:
public class Student {
  public string StudentId { get; set; }
  [Required]
  public string FirstName { get; set; }
  [Required]
  public string LastName { get; set; }
  [Required]
  public string School { get; set; }
}

Resolve "Required" by adding the following using statement at the top of Student.cs:
using System.ComponentModel.DataAnnotations;

Creating the ASP.NET Core Web API student service application

Right-click on the main solution node in Solution Explorer and select: Add >> New Project. Choose the "ASP.NET Core Web Application" template:
Name the Project name: SchoolAPI

Click on Create.

On the next dialog, select ASP.NET Core 2.2 and choose the API template then click on Create:

Our Web API project needs to use the Student class in the SchoolLibrary project. Therefore, we will make a reference from the SchoolAPI project to the SchoolLibrary project. Right-click on the SchoolAPI project node then: Add >> Reference... >> Projects >> Solution >> check SchoolLibrary. Then click on OK.


We need to let the dotnet utility know that we are using .NET Core 2.2 in the SchoolAPI project, even though the default SDK on our computer is a preview version of .NET Core 3.0. This is done by adding a global.json file in the root SchoolAPI folder with the following content:
{
  "sdk": {
    "version": "2.2.103"
  }
}

A quick way of doing this is to run the following CLI command inside the SchoolAPI folder:
dotnet new globaljson --sdk-version 2.2.103

Since we will be using SQL Server, we will need to add the appropriate Entity Framework packages and tooling. From within a terminal window at the root of your StudentAPI project,  run the following commands that will import the appropriate database related packages:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.SqlServer.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools

We need to add a connection string to the database. Add the following to the top of the appsettings.json file in project SchoolAPI:
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=SchoolDB;Trusted_Connection=True;MultipleActiveResultSets=true"
  },

We will be using the Entity Framework Code First approach. The starting point is to create a database context class. Add a C#  class file named SchoolDbContext.cs with the following class code:
public class SchoolDbContext : DbContext {
  public DbSet<Student> Students { get; set; }

  public SchoolDbContext(DbContextOptions<SchoolDbContext> options) : base(options) { }

  protected override void OnModelCreating(ModelBuilder builder) {
    base.OnModelCreating(builder);
    
    builder.Entity<Student>().HasData(
      new { StudentId = Guid.NewGuid().ToString(), 
            FirstName = "Jane", LastName = "Smith", School = "Medicine" },
      new { StudentId = Guid.NewGuid().ToString(), 
            FirstName = "John", LastName = "Fisher", School = "Engineering" },
      new { StudentId = Guid.NewGuid().ToString(), 
            FirstName = "Pamela", LastName = "Baker", School = "Food Science" },
      new { StudentId = Guid.NewGuid().ToString(), 
            FirstName = "Peter", LastName = "Taylor", School = "Mining" }
    );
  }
}

You will need to resolve some of the unrecognized namespaces.

Notice the above code is adding four records of seed data into the database.

In the Startup.cs file in the SchoolAPI project, add the following code to the ConfigureServices() method so that our application can use SQL Server:

services.AddDbContext<SchoolDbContext>(
  option => option.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
We are now ready to apply Entity Framework migrations, create the database and seed data. Remember to build your entire solution before proceeding. Then, from within a terminal window in the SchoolAPI directory, run the following command to create migrations:
dotnet ef migrations add initial_migration

This results in the creation of a migration file ending with the name ....initial_migration.cs in the Migrations folder. In my case, this file looked like this:
using Microsoft.EntityFrameworkCore.Migrations;
namespace SchoolAPI.Migrations {
  public partial class initial_migration : Migration {
    protected override void Up(MigrationBuilder migrationBuilder) {
      migrationBuilder.CreateTable(
        name: "Students",
        columns: table => new {
          StudentId = table.Column<string>(nullable: false),
          FirstName = table.Column<string>(nullable: false),
          LastName = table.Column<string>(nullable: false),
          School = table.Column<string>(nullable: false)
        },
        constraints: table => {
          table.PrimaryKey("PK_Students", x => x.StudentId);
        });

      migrationBuilder.InsertData(
        table: "Students",
        columns: new[] { "StudentId", "FirstName", "LastName", "School" },
        values: new object[,] {
          { "8734ce0b-4337-487a-bd86-102130d47f8d", "Jane", 
            "Smith", "Medicine" },
          { "db356c3a-d745-4f65-9aac-234f706859c2", "John", 
            "Fisher", "Engineering" },
          { "7721c620-a82b-4204-80af-6c7636efc81e", "Pamela", 
            "Baker", "Food Science" },
          { "9a85f51f-160e-4003-9de9-b01354e180a3", "Peter", 
            "Taylor", "Mining" }
        });
    }

    protected override void Down(MigrationBuilder migrationBuilder) {
      migrationBuilder.DropTable(
        name: "Students");
    }
  }
}

Note that the above code also includes commands for inserting sample data.

The next step is to create the SchoolDB database in SQL Server. This is done by running the following command from inside a terminal window at the SchoolAPI folder.

                                dotnet ef database update


If no errors are encountered, we can assume that the database was created and properly seeded with data. Let us create an API controller so that we can see the data that is in our database.  

Right-click on the Controllers node in the SchoolAPI project then: Add >> Controller...

Under Installed >> Common, choose the API tab, highlight "API Controller with actions, using Entity Framework" then click Add.



On the next dialog, make the following choices:

Model class: Student (SchoolLibrary)
Data context class: SchoolDbContext (SchoolAPI)
Controller name: StudentsController


Once you click on Add, the StudentsController.cs file is created in the Controllers folder. To load that particular controller every time you run the SchoolAPI application, edit the launchSettings.json file under the Properties node. Change every instance of "api/values" to "api/students". Now you can run the application by hitting CTRL F5 on your keyboard. This is what you should see in your browser:

[{"studentId":"7721c620-a82b-4204-80af-6c7636efc81e",
  "firstName":"Pamela","lastName":"Baker","school":"Food Science"},
{"studentId":"8734ce0b-4337-487a-bd86-102130d47f8d",
  "firstName":"Jane","lastName":"Smith","school":"Medicine"},
{"studentId":"9a85f51f-160e-4003-9de9-b01354e180a3",
  "firstName":"Peter","lastName":"Taylor","school":"Mining"},
{"studentId":"db356c3a-d745-4f65-9aac-234f706859c2",
  "firstName":"John","lastName":"Fisher","school":"Engineering"}]

Even though our API app seems to be working fine, there is one more thing we need to do. We need to enable CORS (Cross Origin Resource Sharing) so that the service can be accessed from other domains. Add the following code to the ConfigureServices() method in the Startup.cs file found in the SchoolAPI project:

// Add Cors
services.AddCors(o => o.AddPolicy("Policy", builder => {
  builder.AllowAnyOrigin()
    .AllowAnyMethod()
    .AllowAnyHeader();
}));

Add this statement in the Configure() method also in Startup.cs just before app.UseMvc():

            app.UseCors("Policy");

We now have an API and data that we can work with. Let us now see how we can use this data in a Blazor app.

Creating our Client-Side Blazor app

Let us add a client-side Blazor project to our solution. In Solution Explorer, right-click on the Solution node then: Add >> New Project...

Choose the "ASP.NET Core Web Application" template then click on Next.


Name the project SchoolClient, then click on Create.

Choose "ASP.NET Core 3.0" and the "Blazor (client-side)" template then click on Create.

Let us run our Blazor application to see what we have out of the box. Run the SchoolClient application by hitting CTRL F5 on your keyboard. You will see a UI that looks like this:

We will add a Students razor page and menu item to this client-side Blazor application.

Our Blazor project needs to use the Student class in the class library. Therefore, we will make a reference from the Blazor SchoolClient project to the class library project SchoolLibrary. Right-click on the SchoolClient project node then: Add >> Reference... >> Projects >> Solution >> check SchoolLibrary. Then click OK.


Open the _Imports.razor file in the editor and add the following using statement to the bottom of the content so that the Student class is available to a  views:

@using SchoolLibrary

Make a duplicate copy of the FetchData.razor file in the Pages node and call the new file Students.razor. Replace its contents with the following code:

@page "/students"
@inject HttpClient httpClient

<h1>Students</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (students == null) {
<p><em>Loading...</em></p>
} else {
<table class='table table-hover'>
  <thead>
    <tr>
      <th>ID</th>
      <th>First Name</th>
      <th>Last Name</th>
      <th>School</th>
    </tr>
  </thead>
  <tbody>
    @foreach (var item in students) {
    <tr>
      <td>@item.StudentId</td>
      <td>@item.FirstName</td>
      <td>@item.LastName</td>
      <td>@item.School</td>
    </tr>
    }
  </tbody>
</table>
}

@functions {
  Student[] students;
  string baseUrl = "https://localhost:44318/";

  protected override async Task OnInitAsync() {
    await load();
  }

  protected async Task load() {
    students = await httpClient.GetJsonAsync<Student[]>($"{baseUrl}api/students");
  }
}

NOTE: Make sure you adjust the value of baseUrl to match the URL of your SchoolAPI service.

Let us focus on the @functions block. The OnInitAsyns() method is called when the page gets loaded. It calls a local load() method. The load() method loads a students array with data from our SchoolAPI service. The remaining HTML/Razor code simply displays the data in a table.

Let us add a menu item to the left-side navigation of our client application. Open Shared/NavMenu.razor in the editor and add the following <li> to the <ul> block (around line 24):

<li class="nav-item px-3">
  <NavLink class="nav-link" href="students">
    <span class="oi oi-list-rich" aria-hidden="true"></span> Students
  </NavLink>
</li>

You must be looking forward to testing our client-side application. To run your app, highlight the SchoolAPI node then hit CTRL F5 to run the server-side application without debugging. This starts the API service.

Next, we will run the client-side Blazor application. Highlight the SchoolClient node then hit CTRL F5 to run the blazor app. This is what it should look like when you click on Students:


Adding data using the POST method
The client side application will not be complete without adding to it add, edit and delete funtionality. We shall start with adding data. Place the following instance variables just above the OnInitAsyc() method:

string studentId;
string firstName;
string lastName;
string school;

We need an HTML form to add data. Add the following RAZOR code just before @functions:

@if (students != null) // Insert form
{
  <input placeholder="First Name" bind="@firstName" /><br />
  <input placeholder="Last Name" bind="@lastName" /><br />
  <input placeholder="School" bind="@school" /><br />
  <button onclick="@Insert" class="btn btn-warning">Insert</button>
}

When the Insert button is clicked, an Insert() method is called. Add the following Insert() method inside the @functions() block that makes a POST request to the API service:
protected async Task Insert() {
  string endpoint = $"{baseUrl}api/students";

  Student student = new Student() {
    StudentId = Guid.NewGuid().ToString(),
    FirstName = firstName,
    LastName = lastName,
    School = school
  };

  await httpClient.SendJsonAsync(HttpMethod.Post, endpoint, student);
  ClearFields();
  await load();
  StateHasChanged(); // causes the page to get automatically refreshed
}

After data is added, the above code clears the fields then loads the data again in the HTML table. Add the following ClearFields() method:

protected void ClearFields() {
  studentId = string.Empty;
  firstName = string.Empty;
  lastName = string.Empty;
  school = string.Empty;
}

Run the Blazor client-side application and select Students from the navigation menu. This is what it should look like:



I entered David, Strong and Entertainment for data and when I clicked on Insert I got the following data added to the database:
Updating & Deleting data using the PUT & DELETE methods
To distinguish between INSERT and EDIT/DELETE mode, we shall add an enum to our code. Add the following to the list of instance variables:

private enum MODE { None, Add, EditDelete };
MODE mode = MODE.None;
Student student;

We will add a button at the top of our table dedicated to adding data. Add the following markup just above the opening <table> tag:

<button onclick="@Add"  class="btn btn-success">Add</button>

Let us add the following Add() method:

protected void Add() {
  ClearFields();
  mode = MODE.Add;
}

Around line 34, change the @if (students != null) statement to:

@if (students != null && mode==MODE.Add)

If you run the application, the form for inserting data only displays when the Add button is clicked:
After we successfully insert data, we want the insert form to disappear. Add the following code to the bottom of the Insert() method:
mode = MODE.None;
Let us now add a form that only appears when we wish to update or delete data. Add the following just before @functions:

@if (students != null && mode==MODE.EditDelete) // Update & Delete form
{
  <input type="hidden" bind="@studentId" /><br />
  <input placeholder="First Name" bind="@firstName" /><br />
  <input placeholder="Last Name" bind="@lastName" /><br />
  <input placeholder="School" bind="@school" /><br />
  <button onclick="@Update" class="btn btn-primary">Update</button>
  <span>&nbsp;&nbsp;&nbsp;&nbsp;</span>
  <button onclick="@Delete" class="btn btn-danger">Delete</button>
}

Add these Update() and Delete() methods as shown below:

protected async Task Update() {
  string endpoint = $"{baseUrl}api/students/{studentId}";

  Student student = new Student() {
    StudentId = studentId,
    FirstName = firstName,
    LastName = lastName,
    School = school
  };

  await httpClient.SendJsonAsync(HttpMethod.Put, endpoint, student);
  ClearFields();
  await load();
  mode = MODE.None;
  StateHasChanged(); // causes the page to get automatically refreshed
}

protected async Task Delete() {
  string endpoint = $"{baseUrl}api/students/{studentId}";
  await httpClient.SendJsonAsync(HttpMethod.Delete, endpoint, null);
  ClearFields();
  await load();
  mode = MODE.None;
  StateHasChanged(); // causes the page to get automatically refreshed
}

We want to be able to select a row of data and update it. We can add an onclick handler to a row. In the HTML table, replace the <tr> opening tag under the @foreach statement with this:

<tr onclick="@(() => Show(item.StudentId))">

The above would pass the appropriate studentId to a method named Show() whenever a row is clicked. Add the following Show() method that retrieves a student from the API service and displays it in the Update/Delete form:

protected async Task Show(string id) {
  student = await httpClient.GetJsonAsync<Student>($"{baseUrl}api/students/{id}");
  studentId = student.StudentId;
  firstName = student.FirstName;
  lastName = student.LastName;
  school = student.School;
  mode = MODE.EditDelete;
}

Let us run the application and test it out for adding, updating and deleting data. Run the application:
Click on the Add button to insert data. 
Enter data then click on the Insert button. The data should get added to the database:

To update the data, click on a row. The selected data should display in the update/delete form.

I selected Bill Black and changed Black to White then clicked on Update. Last Name was successfully changed to White.

Lastly, let us delete a record. I clicked on the Bill White row. Data was displayed in the Update/Delete form.
When I clicked on Delete, Bill White get deleted.
I hope you found this tutorial useful. I expect Blazor to change by the time it is generally available in release mode. I will do my best to update this article with any breaking changes.

Thursday, March 28, 2019

Inspecting your .NET classes with .NET Object Model Diagram Generator

OmgGenerator


While at the MVP summit in March 2019, I attended a short presentation by Morten Nielson, a fellow MVP. He talked about a utility he created that facilitates inspection & documentation of .NET classes. You can visit his GitHub project at https://github.com/dotMorten/DotNetOMDGenerator/blob/master/README.md.

There is much more detail about his utility at Morton's GitHub site. I will, however, give you a very stripped down version of what this utility is capable of doing in this article. To start with, download and globally install the OmgGenerator with:


dotnet tool install --global dotMorten.OmdGenerator


Once you have installed the OmgGenerator utility, you can inspect .NET classes in your application by running the following command in the root of your .NET project:

generateomd /source=. /output=diagram

I run the above commamd in one of my simple projects and this is what was created in a file named diagram.html:

 
Thev default output format is HTML. You can generate the output in markdown format with the /fotmat switch as shown below:

generateomd /source=. /output=diagram /format=md

The above would produce a file named diagram.md with the same diagram as before.
You can get the utility to exclude certain directories with the /exclude switch. The following would exclude folders bin, tests, Views and Areas:

generateomd /source=. /output=diagram /exclude="*/bin/*;*/tests/*;*/Views/*;*/Areas/*"

Another very powerful feature is the tool’s ability to identify the class differences between two branches in GitHub. In the following example I am comparing the master branch and the ver2 branch in a GitHub repo:

generateomd /output=difs /source=https://github.com/medhatelmasry/HealthAPI/archive/ver2.zip /compareSource=https://github.com/medhatelmasry/HealthAPI/archive/master.zip /exclude="*/ref/*;*/tests/*;*/perftests/*"

The diff.html file produced looks like this:

The above tells me the following:
  • the Symptoms property was added to the Ailment class
  • a new Hospital class was added
  • the Supplier property was added to the Medication class

Conclusion

I am impressed with Morten's tool and would recommend it to any .NET programmer out there.


Wednesday, February 13, 2019

Deploy Node Express Docker Container to Azure

In this tutorial I will show you how to easily deploy a simple Node-Express docker image and run it from Azure containers. It is assumed that you have:
  • Docker installed on your computer
  • Git installed on your computer
  • You have a billable Azure account
  • You have a Docker hub account
  • You have installed the Azure CLI on your computer
Let us start with a very simple Node-Express application. I have one that you can clone from GitHub. Run the following command from a working directory in order to clone my simple Node-Express application:
Once you have cloned the application, you can create a Docker image named toon-node. View a file named Dockerfile located in the root of the application. This file looks like this:
# Create image based on the official Node 6 image from the dockerhub
FROM node:10.13.0-jessie

# Create a directory where our app will be placed
RUN mkdir -p /usr/src/app

# Change directory so that our commands run inside this new directory
WORKDIR /usr/src/app

# Copy dependency definitions
COPY package.json /usr/src/app

# Install dependecies
RUN npm install

# Get all the code needed to run the app
COPY . /usr/src/app

# Expose the port the app runs in
ENV PORT 80

# Serve the app
CMD ["npm", "start"]
This is what each command in Dockerfile does.
FROM node:10.13.0-jessie Base the new image on node:10:13.0-jessie
RUN mkdir -p /usr/src/app Create a directory /usr/src/app in the container
WORKDIR /usr/src/app Make /usr/src/app the working dir in the container
COPY package.json /usr/src/app Copy package.json on your computer into the working directory in the container
RUN npm install Execute "npm install" in the container
COPY . /usr/src/app Copy all files in the. current directory of your computer into the container’s working directory. Note that any files in .dockerignore will be ignored
ENV PORT 80 The node application inside the container will listen in on port 80
CMD ["npm", "start"] Launch the web application by executing “npm start”.
Create a Docker image with the following terminal command:
docker build -f Dockerfile -t snoopy/toons:toon-node . --no-cache
Note that you must prefix the image name with your Docker hub account. In the above example, snoopy represents your username on Docker hub.
Once the image is created, you can verify that it indeed exists by using the following Docker terminal command that lists on the images on your computer:
docker images
You should see an image named toons:toon-node prefixed by your Docker hub username.
Now let us run this Node-Express application to see what it does. We will create a container from the image that we just created. Run the following command to create a container whereby port 80 in the container is mapped to port 3000 on your computer:
docker run -p 3000:80 snoopy/toons:toon-node
You can then point your browser to http://localhost:3000 to see what the node-express application looks like. If all goes well, you should see the following web page:
 
The next thing we need to do is push our image to Docker hub. This is done by first logging-in into Docker hub with:
docker login
Note that this needs to be done only once on the computer.
You can then push the image to Docker hub with:
docker push snoopy/toons:toon-node
If you go to Docker hub at https://hub.docker.com/, you will see that the image exists in the toons repository. This is what my toons repository looked like on Docker hub:
With our image sitting in the Docker hub repository, we can now start using Azure to pull the image and run a container in Azure.
Login into Azure on your computer using the Azure CLI (command line interface). This is done by running the following terminal window command:
az login
This opens a browser window in which you will be authenticated. You can close the browser once you have successfully authenticated. 
We need to either use an existing resource group or. create a new one. I created a new resource group named toon-rg in data centre West-US2 with the following terminal window command:
az group create --location westus2 --name toon-rg 
Once you execute the above command, a successful response from the server will look similar to this:
{
  "id": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/toon-rg",
  "location": "westus2",
  "managedBy": null,
  "name": "toon-rg",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": null
}

Let us use the image we have in Docker hub to run a container in Azure. This can be done by executing the following command:
az container create --name toon-c --resource-group toon-rg --image snoopy/toons:toon-node --ip-address public
These are what the above settings represent:
--name toon-c The container name is “toon-c".
--resource-group toon-rg The resource group name is “toon-rg".
--image snoopy/toons:toon-node The name of our image on Docker hub is "snoopy/toons:toon-node".
--ip-address public Respond with the public IP address so that we can run and verify that indeed our web site is working on Azure.
Remember that you must replace snoopy with your Docker hub username.
When I ran the last command, it took some time to run and I eventually got a long JSON response. I am showing you a part of the response that has the public IP address:
You can see the public IP address in the JSON response. If you point your browser to this IP address you should see the application running on Azure. This is what I experienced when I pointed my browser to http://52.247.209.239
It is rather impressive how easy it is to host a Docker container on Azure.