Saturday, December 18, 2021

Using Google Charts API with an ASP.NET Core 6.0 MVC app

Google Charts is a free JavaScript API that you can use to generate good looking charts on a web page. Although it has nothing to do with C# and .NET, we can still use it in an ASP.NET application. In this article, I will show you how to generate five types of charts to display dynamically generated data. The source of data will be the well known Northwind database running in a Docker container.

In another article, I show how to use Google charts with an ASP.NET Core Razor Pages application.  In this article, I work with the ASP.NET MVC template, instead.

Source code: https://github.com/medhatelmasry/gChartMVC

The environment I am using is:

  • Windows 11
  • Docker Desktop for Windows
  • .NET version 6.0.100
  • Visual Studio Code

Start Northwind database in a Docker container

To pull & run the Northwind database in a Docker container, run the following command in a terminal window:

docker run -d --name nw -p 1444:1433 kcornwall/sqlnorthwind

The above command does the following:

Docker image: kcornwall/sqlnorthwind
Container Name (--name): nw
Ports (-p): Port 1433 in container is exposed as port 1444 on the host computer
Password: The sa password is Passw0rd2018. This was determined from the Docker Hub page for the image.
-d: Starts the container in detached mode

This is what I experienced after I ran the above command:


Let us make sure that the container is running. Execute this command to ensure that the container is indeed running.

docker ps

The following confirmed to me that the container is running:

Project setup

Run the following command to create an ASP.NET Core MVC application using .NET 6.0 in a folder named gChartMVC:

dotnet new mvc -f net6.0 -o gChartMVC

Change directory into the new folder and open the project inside VS Code with the following commands:

cd gChartMVC 

code .

We will need to install an Entity Framework command-line utility. If you have not done so already, install dotnet-ef with this command:

dotnet tool install –g dotnet-ef 

It does not hurt to upgrade this tool to the latest version with:

dotnet tool update -g dotnet-ef

Also, from within the root folder of your project, add some SQL-Server and Entity Framework related packages with the following terminal-window commands:

dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

In appsettings.json, add this to ConnectionStrings block just before “Logging”:

"ConnectionStrings": {
    "NW": "Data Source=localhost,1444;Initial Catalog=Northwind;Persist Security Info=True;User ID=sa;Password=Passw0rd2018"
},

Next, let us reverse engineer the Products & Categories entities in the Northwind database. Execute this command from the root of your project:

dotnet-ef dbcontext scaffold "Data Source=localhost,1444;Initial Catalog=Northwind;Persist Security Info=True;User ID=sa;Password=Passw0rd2018" Microsoft.EntityFrameworkCore.SqlServer -c NorthwindContext -o NW --table Products --table Categories

This creates a NW folder in your project with entities Category & Product. It also adds the database context class NorthwindContext.



Delete the OnConfiguring() method in NorthwindContext.cs so that there is no hard-coded connection string in our C# code.

Add the following code to Program.cs right after where the variable builder is declared:

var connectionString = builder.Configuration.GetConnectionString("NW");
builder.Services.AddDbContext<NorthwindContext>(options => {
  options.UseSqlServer(connectionString);
});

Reading data

Let's take advantage of dependency injection to access the database through an instance of the NorthwindContext. Add the following instance variable declaration to the top of the HomeController class:

private readonly NorthwindContext _northwindContext;

Update the HomeController constructor so it looks like this:

public HomeController(ILogger<HomeController> logger, NorthwindContext northwindContext) {
  _logger = logger;
  _northwindContext = northwindContext;
}

We will next add a method to the HomeController that can be called from our JavaScript front-end that reads products by category. 

public async Task<JsonResult> ChartData() {
   var query = await _northwindContext.Products
     .Include(c => c.Category)
     .GroupBy(p => p.Category!.CategoryName)
     .Select(g => new
     {
         Name = g.Key,
         Count = g.Count()
     })
     .OrderByDescending(cp => cp.Count)
        .ToListAsync();

   return Json(query);
}

At this stage, let's run our web application and verify that we are indeed able to read data from the Northwind database and subsequently generate JSON data. Run your application with:

dotnet watch run

Point your browser to https://localhost:7108/home/chartdata

NOTE: you will need to adjust the port number to suit your environment.

This is what was revealed in my browser:


We have a sense of assurance that our data is ready to be displayed in a chart.

Charting the data

Replace your Views/Home/Index.cshtml with the following code:

<title>@ViewData["Title"] - Google Charts</title>  
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>  
  
<div id="column_chart_div"></div>  
<div id="line_chart_div"></div>  
<div id="pie_chart_div"></div>  
<div id="area_chart_div"></div>  
<div id="bar_chart_div"></div>  
<script type="text/javascript">  
  
   google.charts.load('current', {  
     packages: ['corechart', 'bar']  
   });  
   google.charts.setOnLoadCallback(LoadData);  
   function LoadData() {  
      $.ajax({  
         url: '/Home/ChartData',  
         dataType: "json",  
         type: "GET",  
         error: function(xhr, status, error) {  
            toastr.error(xhr.responseText);  
         },  
         success: function(data) {  
            PopulationChart(data, "column-chart");  
            PopulationChart(data, "line-chart");  
            PopulationChart(data, "pie-chart");  
            PopulationChart(data, "area-chart"); 
            PopulationChart(data, "bar-chart"); 
            return false;  
         }  
      });  
      return false;  
   }  
   function PopulationChart(data, chart_type) {  
      var dataArray = [  
         ['Category', 'Product']  
      ];  
      $.each(data, function(i, item) {  
         dataArray.push([item.name, item.count]);  
      });  
      var data = google.visualization.arrayToDataTable(dataArray);  
      var options = {  
         title: 'Product count by category',  
         chartArea: {  
             width: '80%'  
         },  
         colors: ['#b0120a', '#7b1fa2', '#ffab91', '#d95f02'],  
         hAxis: {  
             title: 'Categories',  
             minValue: 0  
         },  
         vAxis: {  
             title: 'Product Count'  
         }  
      };  
      var chart;
      switch(chart_type) {
         case "line-chart":
            chart = new google.visualization.LineChart(document.getElementById('line_chart_div'));  
            break;
         case "pie-chart":
            chart = new google.visualization.PieChart(document.getElementById('pie_chart_div'));  
            break;
         case "area-chart":
            chart = new google.visualization.AreaChart(document.getElementById('area_chart_div'));  
            break;
         case "bar-chart":
            chart = new google.visualization.BarChart(document.getElementById('bar_chart_div'));  
            break;
         default:
            chart = new google.visualization.ColumnChart(document.getElementById('column_chart_div'));  
            break;
      }
      chart.draw(data, options);  
      return false;  
   }  
</script>  

If you point your browser to the home page, you should see five charts, namely: column, line, pie, area and bar charts.


Conclusion

I trust the above article helps you consider using Google Charts with ASP.NET MVC apps to visually display data.




Friday, December 17, 2021

Explore .NET MAUI Blazor Apps with .NET 6.0 & Visual Studio 2022 Version 17.1.0 Preview 1.1

In a previous article, I wrote about .NET MAUI Apps. In this article, I will discuss the Blazor version of .NET MAUI Apps. This is known as .NET MAUI Blazor Apps.

MAUI is not yet officially released. The current bits offer a glimpse into what the final product will look like. 

This is the environment that I am using:

  • Windows 11 Version 21H2
  • Visual Studio 2022 Version 17.1.0 Preview 1.1
  • .NET 6.0.101
Source code for this application can be found at: https://github.com/medhatelmasry/FirstMauiBlazorApp

Setup

You will find installation instructions for .NET MAUI at: https://docs.microsoft.com/en-us/dotnet/maui/get-started/installation

The only workload I installed in Visual Studio 2022 (Preview) is "Mobile development with .NET", as shown below:


It is also worth noting that I do not have any other Android development application (like Android Studio) installed on my computer. The above Visual Studio 2022 workload also installed an Android emulator.

Application

Let's get started exploring what apps we can develop with .NET MAUI. Start Visual Studio 2022 (Preview) and select "Create a new project":


Enter "maui" in the filter field. You will discover that there are three MAUI-related projects that you can create - namely: 
  1. .NET MAUI App
  2. .NET MAUI Blazor App
  3. .NET MAUI Class Library
In this article, I explore the second in the above list - .NET MAUI Blazor App. Select this project type then click Next:




I named my application FirstMauiBlazorApp:




Let's firstly run our app on Windows. From the drop-down-list at the top, make sure you have chosen "Windows Machine".


Click on "Windows Machine" to run the application. Soon after, you should experience the following application running on your desktop:


Stop the application by either closing it or clicking on the red square button on the top of Visual Studio 2022. You will find that the application is installed in your "Apps and Features" on windows. You can, of course, uninstall it if you so desire.

Adding our own page

Add a "Razor Component..." to the Pages folder of your application.


I named my file Toons.razor.

We will modify Toons.razor so that it reads an online API that contains some cartoon characters. If you point your browser to https://apipool.azurewebsites.net/api/toons it will show the following data:

[{"id":1,"lastName":"Flintstone","firstName":"Fred","occupation":"Mining Manager","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/fred.png","votes":0},{"id":2,"lastName":"Rubble","firstName":"Barney","occupation":"Mining Assistant","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/barney.png","votes":0},{"id":3,"lastName":"Rubble","firstName":"Betty","occupation":"Nurse","gender":"F","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/betty.png","votes":0},{"id":4,"lastName":"Flintstone","firstName":"Wilma","occupation":"Teacher","gender":"F","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/wilma.png","votes":0},{"id":5,"lastName":"Rubble","firstName":"Bambam","occupation":"Baby","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/bambam.png","votes":0},{"id":6,"lastName":"Flintstone","firstName":"Pebbles","occupation":"Baby","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/pebbles.png","votes":0},{"id":7,"lastName":"Flintstone","firstName":"Dino","occupation":"Pet","gender":"F","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/dino.png","votes":0},{"id":8,"lastName":"Mouse","firstName":"Micky","occupation":"Hunter","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/disney/MickyMouse.png","votes":0},{"id":9,"lastName":"Duck","firstName":"Donald","occupation":"Sailor","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/disney/DonaldDuck.png","votes":0}]

Each JSON object contains the following properties:

id (int)
lastName (string)
firstName (string)
occupation (string)
gender (string)
pictureUrl (string)
votes (int)

Replace the content of Toons.razor with the following code:

@page "/toons"

@using System.Text.Json
@using System.Text.Json.Serialization

<h1>Toon Characters</h1>

@if (toonList == null) {
  <p><em>Loading...</em></p>
} else {
  <table class="table">
    <tbody>
      @foreach (var item in toonList) {
        <tr>
          <td>@item.FullName</td>
          <td><img src="@item.PictureUrl" style="height: 40px" alt="@item.FirstName @item.LastName"> </td>
        </tr>
      }
    </tbody>
  </table>
}

@code {
  private Toon[] toonList;

  protected override async Task OnInitializedAsync() {
      HttpClient client = new HttpClient();
      var stream = client.GetStreamAsync("https://apipool.azurewebsites.net/api/toons");
      toonList = await JsonSerializer.DeserializeAsync<Toon[]>(await stream);
  }

  public class Toon {
    [JsonPropertyName("id")]
    public int Id { get; set; }

    [JsonPropertyName("lastName")]
    public string LastName { get; set; }

    [JsonPropertyName("firstName")]
    public string FirstName { get; set; }

    [JsonPropertyName("occupation")]
    public string Occupation { get; set; }

    [JsonPropertyName("gender")]
    public string Gender { get; set; }

    [JsonPropertyName("pictureUrl")]
    public string PictureUrl { get; set; }

    [JsonPropertyName("votes")]
    public int Votes { get; set; }

    public string FullName {
      get {
        return string.Format("{0} {1}", this.FirstName, this.LastName);
      }
    }
  } 

Finally, edit the home page, Index.razor, so that it displays cartoon characters. This is done by updating Index.razor so that it looks like this:

@page "/"

<Toons />

Run your application and you will see the following output:


Let us see what this app looks like in an android emulator. To setup an emulator, choose Tools >> Android >> Android Device Manager...


You can configure an Android device of your choice. In my case, even though I configured both Pixel 4 & Pixel 5, I found Pixel 4 to be more cooperative.


You can start the emulator of your choice from within the Android Device Manager.

Choose the android emulator of your choice in the run drop-down-list at the top of Visual Studio 2022:


Run your app in the Android emulator. This is what it should look like:

If you have built some application using Blazor, here is an opportunity for you to migrate some of that functionality into the mobile world.

Thursday, December 16, 2021

Explore .NET MAUI Apps with .NET 6.0 & Visual Studio 2022 Version 17.1.0 Preview 1.1

The birth of .NET MAUI among the family of .NET products is very exciting. Although we did have Xamarin to develop cross-platform mobile applications in the past, .NET MAUI is a little different because it uses a unified version .NET 6.0 for mobile devices and desktop computers.

MAUI is not yet officially released. The current bits offer a glimpse into what the final product will look like. In this article, I will introduce you to .NET MAUI Apps. 

Meantime, this is the environment that I am using:

  • Windows 11 Version 21H2
  • Visual Studio 2022 Version 17.1.0 Preview 1.1
  • .NET 6.0.101
Source code for this application can be found at: https://github.com/medhatelmasry/FirstMauiApp

Setup

You will find installation instructions for .NET MAUI at: https://docs.microsoft.com/en-us/dotnet/maui/get-started/installation

The only workload I installed in Visual Studio 2022 (Preview) is "Mobile development with .NET", as shown below:


It is also worth noting that I do not have any other Android development application (like Android Studio) installed on my computer. The above Visual Studio 2022 workload also installed an Android emulator.

Application

Let's get started exploring what apps we can develop with .NET MAUI. Start Visual Studio 2022 (Preview) and select "Create a new project":


Enter "maui" in the filter field. You will discover that there are three MAUI-related projects that you can create - namely: 
  1. .NET MAUI App
  2. .NET MAUI Blazor App
  3. .NET MAUI Class Library
In this tutorial, I will explore the first in the above list - .NET MAUI App. Select this project type then click Next:


I named my application FirstMauiApp:


Firstly, let's run our app on Windows. From the drop-down-list at the top of Visual Studio 2022, make sure you have chosen "Windows Machine".


Click on "Windows Machine" to run the application. Soon after, you should experience the following application running on your desktop:


Stop the application by either closing it or clicking on the red square button on the top of Visual Studio 2022. You will find that the application gets installed in your "Apps and Features" on windows. You can, of course, uninstall it if you so desire.

Adding our own page

Add “.NET MAUI ContentPage (Preview)” named ToonPage.xaml:


Edit App.xaml.cs so that it starts ToonPage instead of MainPage.

MainPage = new ToonPage();

Run the application. It should look like this:

Stop the application.

We will modify ToonPage so that it reads an online API that contains some cartoon characters. If you point your browser to https://apipool.azurewebsites.net/api/toons it will show the following data:

[{"id":1,"lastName":"Flintstone","firstName":"Fred","occupation":"Mining Manager","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/fred.png","votes":0},{"id":2,"lastName":"Rubble","firstName":"Barney","occupation":"Mining Assistant","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/barney.png","votes":0},{"id":3,"lastName":"Rubble","firstName":"Betty","occupation":"Nurse","gender":"F","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/betty.png","votes":0},{"id":4,"lastName":"Flintstone","firstName":"Wilma","occupation":"Teacher","gender":"F","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/wilma.png","votes":0},{"id":5,"lastName":"Rubble","firstName":"Bambam","occupation":"Baby","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/bambam.png","votes":0},{"id":6,"lastName":"Flintstone","firstName":"Pebbles","occupation":"Baby","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/pebbles.png","votes":0},{"id":7,"lastName":"Flintstone","firstName":"Dino","occupation":"Pet","gender":"F","pictureUrl":"https://api4all.azurewebsites.net/images/flintstone/dino.png","votes":0},{"id":8,"lastName":"Mouse","firstName":"Micky","occupation":"Hunter","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/disney/MickyMouse.png","votes":0},{"id":9,"lastName":"Duck","firstName":"Donald","occupation":"Sailor","gender":"M","pictureUrl":"https://api4all.azurewebsites.net/images/disney/DonaldDuck.png","votes":0}]

Each JSON object contains the following properties:

id (int)
lastName (string)
firstName (string)
occupation (string)
gender (string)
pictureUrl (string)
votes (int)

To represent the above data, we will add a class named Toon with the following content:

public class Toon {
  [JsonPropertyName("id")]
  public int Id { get; set; }

  [JsonPropertyName("lastName")]
  public string LastName { get; set; }

  [JsonPropertyName("firstName")]
  public string FirstName { get; set; }

  [JsonPropertyName("occupation")]
  public string Occupation { get; set; }

  [JsonPropertyName("gender")]
  public string Gender { get; set; }

  [JsonPropertyName("pictureUrl")]
  public string PictureUrl { get; set; }

  [JsonPropertyName("votes")]
  public int Votes { get; set; }

  public string FullName {
    get {
        return string.Format("{0} {1}", this.FirstName, this.LastName);
    }
  }

  public override string ToString() {
    return string.Format($"{Id}\t{FullName}\t{Occupation}\t{Gender}\t{PictureUrl}\t{Votes}");
  }
}

You will need to resolve the appropriate namespace for JsonPropertyName. This will add the following to your using statements:

using System.Text.Json.Serialization;

We will need to update the UI file named ToonPage.xaml, so that we  can display the contents of the Toon[] array. In ToonPage.xaml, replace the existing <Label .... /> control with:

<CollectionView x:Name="cvToons" ItemsLayout="VerticalGrid, 2">
  <CollectionView.ItemTemplate>
    <DataTemplate>
      <Grid Padding="10" RowDefinitions="60" ColumnDefinitions="70,*">
        <Image Grid.RowSpan="2" 
        Source="{Binding PictureUrl}" 
        Aspect="AspectFit"
        HeightRequest="60" 
        WidthRequest="60">
          <Image.Clip>
            <RectangleGeometry Rect="0,0,160,160"/>
          </Image.Clip>
        </Image>

        <Label Grid.Column="1" 
        Text="{Binding FullName}" 
        FontAttributes="Bold"
        TextColor="Black"
        VerticalOptions="Start"
        LineBreakMode="TailTruncation" />

        <Label Grid.Column="1" 
        Text="{Binding Occupation}"
        LineBreakMode="TailTruncation"
        FontAttributes="Italic" 
        TextColor="Black"
        VerticalOptions="End" />

      </Grid>
    </DataTemplate>
  </CollectionView.ItemTemplate>
</CollectionView>

Next, add the following method to your ToonPage.xaml.cs file, which reads the content of the API, hydrates it into an array of Toon objects Toon[], then binds the data to the CollectionView control with ID cvToons:

public async void GetToonsAsync() {
  HttpClient client = new HttpClient();
  var stream = client.GetStreamAsync("https://apipool.azurewebsites.net/api/toons");
  var data = await JsonSerializer.DeserializeAsync<Toon[]>(await stream);
  Dispatcher.Dispatch(() => cvToons.ItemsSource = data);
}

You will need to resolve the namespace for the JsonSerializer class, which is:

using System.Text.Json;

Finally, append a call to the above method in the constructor by adding this statement just below InitializeComponent():

GetToonsAsync();

Run your application and you will see the following output:


Let us see what this app looks like in an android emulator. To setup an emulator, choose Tools >> Android >> Android Device Manager...


You can configure an Android device of your choice. In my case, even though I configured both Pixel 4 & Pixel 5, I found Pixel 4 to be more cooperative.


You can start the emulator of your choice from within the "Android Device Manager".

Choose the android emulator of your choice in the run drop-down-list at the top of Visual Studio 2022:


Run your app in the Android emulator. This is what it should look like:

I hope you found this useful. I am sure this product will evolve and, perhaps, change by the time it is finally released.

Sunday, March 21, 2021

Create a docker image for a React app

 This tutorial will show you how easy it is to create a docker image for a react application. Lets get started.

Companion Video: https://youtu.be/15HtGddstok

Pre-requisites

You need to have the following installed on your computer:
  • Node.js & npm
  • Docker desktop

Create a React app

Choose a suitable working folder and then execute the following command in a terminal window to create a React app named react101:

npx create-react-app react101 --use-npm

cd react101 

npm start

The app will display in your default browser as shown below:


Build the React app

Stop the app by typing CTRL C. Execute the following command from a terminal window in the application's root folder to create a deployable version of the application.

npm run build

This creates the deployable artifacts in the /build folder and looks like this:

Containerize React app

We will use the nginx web server docker base image. Create a text file named Dockerfile in your application's root folder and add to it the following:

FROM nginx:alpine 
WORKDIR /var/www/web
COPY build .
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

The nginx.conf configuration file is used to set the nginx document root . Add the following code to nginx.conf:

events { }
http {
  include mime.types;
  server {
    listen 80;
    index index.html;
    location / {
      root /var/www/web;
      try_files $uri $uri/ /index.html =404;
    }
  }
}

We can now build a docker image by running the following command:

docker build --tag react:1.0.0 .

You should see your docker image when you run the following docker command:

docker images

Your output will look similar to this:
react  1.0.0  8fa6896c8bb4   About a minute ago   23.1MB

Let's run the image as a container and see if we can load our react app in a browser. Run the following command to run the web app on port 8888 in a container:

docker run -d -p 8888:80 react:1.0.0

Point your browser to http://localhost:8888 and you should see your web app being served from a docker container.

Saturday, February 27, 2021

Build REST API using EF with Azure Functions v3 & .NET Core 3.1

In this tutorial I will demonstrate how to build a REST API application using Azure Functions v3. The application we will build together uses Entity Framework Core Migrations, Dependency Injection and .NET Core 3.1. We will use the light-weight VS Code editor so that you can go through this tutorial on Windows 10, Mac or Linux.

Source code: https://github.com/medhatelmasry/AzureFunctionsEF.git

Install the appropriate Azure CLI for your operating system from https://docs.microsoft.com/en-us/cli/azure/install-azure-cli.

You need to install the Azure Functions extension for Visual Studio Code before proceeding with this tutorial. Once the extension is installed, you will find it among your extensions.


In your working directory, create the following folder structure:


Inside the AzureFunctionsEF directory, execute the following terminal window commands:

dotnet new sln
dotnet new classlib -f netcoreapp3.1 -o DataLayer
dotnet sln add DataLayer/DataLayer.csproj

Start Visual Studio Code.  Under the Functions tab, select your Azure subscription. You will then be able to create a new Azure Functions project.


Choose the AzureFunctionsEF/Functions folder.


Select C#.
Select HttpTrigger.


Name your Azure function HttpWebAPI.


Give your function app the namespace Snoopy.Function.



Select Anonymous for access rights.



Finally, select "Open in current window".

Let us see what the app does. Enter the following in the terminal window inside the AzureFunctionsEF directory:

cd Functions
func start

The following will appear:


Copy the URL (http://localhost:7071/api/HttpWebAPI) and paste it in the address line of your browser. You will see a response like this:


The message in your browser suggests that you should pass a name query string. I appended the following to the URL: ?name=Superman. I got the following result:


Hit CTRL C to terminate the running app.

Back in the terminal window inside the AzureFunctionsEF directory, execute the following commands:

dotnet sln add Functions/Functions.csproj
dotnet add Functions/Functions.csproj reference DataLayer/DataLayer.csproj


DataLayer class library project

We will work on the DataLayer project by adding a Student class, an Entity Framework database context class, a connection string, and a class that is capable of reading configurations settings.

Add the following packages to the DataLayer project by executing the following commands in a terminal window inside the DataLayer folder:

dotnet add package Microsoft.EntityFrameworkCore -v 3.1.9
dotnet add package Microsoft.EntityFrameworkCore.Tools -v 3.1.9
dotnet add package Microsoft.EntityFrameworkCore.Design -v 3.1.9
dotnet add package Microsoft.EntityFrameworkCore.SqlServer -v 3.1.9
dotnet add package Microsoft.Extensions.Configuration.Json -v 3.1.9
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables -v 3.1.9 

Open the DataLayer folder in Visual Studio Code.

Delete DataLayer/Class1.cs.

Add a Models folder. Inside the Models folder, add a C# Student class file with 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; }
}

Also, in the Models folder, add another C#  Config class file with the following code:

public static class Config {
    private static IConfiguration configuration;
    static Config() {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables();

        configuration = builder.Build();
    }

    public static string Get(string name) {
        string appSettings = configuration[name];
        return appSettings;
    }
}

Now let us add a settings file named local.settings.json right inside the root DataLayer folder with the following content:

{
  "DefaultConnection":"Server=(localdb)\\mssqllocaldb;Database=SchoolDB;Trusted_Connection=True;MultipleActiveResultSets=true"
}

Since we are using Entity Framework, we need to add a database context class. Add the following ApplicationDbContext.cs class to the DataLayer root directory:

public class ApplicationDbContext : DbContext {
  public DbSet<Student> Students { get; set; }

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

  public ApplicationDbContext(DbContextOptions options) : base(options) { }

  public ApplicationDbContext() : base() { }

  protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlServer(Config.Get("DefaultConnection"));

  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"
      }
    );
  }
}

Now that we have out models and database context classes in place, let us go ahead and run Entity Framework Migrations. In a terminal window inside the DataLayer root folder, execute the following commands:

dotnet-ef migrations add M1 -o Data/Migrations
dotnet-ef database update

At this point, if all goes well, the database would be created and seeded with sample data.


Azure Functions Project

Add the following package to the Functions project by executing the following command in a terminal window inside the Functions folder:

dotnet add package Microsoft.Extensions.Http -v 3.1.10

Open the Functions folder in Visual Studio Code.

Add the following connection string to local.settings.json.

"DefaultConnection":"Server=(localdb)\\mssqllocaldb;Database=SchoolDB;Trusted_Connection=True;MultipleActiveResultSets=true",

We will use dependency injection to access the database context and HttpClient objects. Therefore, create a Startup.cs file in the root Functions folder and add to it this code:

[assembly: WebJobsStartup(typeof(StartUp))]
namespace Functions {
  public class StartUp : IWebJobsStartup {
    public void Configure(IWebJobsBuilder builder) {
      builder.Services.AddDbContext<ApplicationDbContext>(options1 => {
        options1.UseSqlServer(
          Config.Get("DefaultConnection"),
          builder =>
          {
            builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), null);
            builder.CommandTimeout(10);
          }
        );
      });

      builder.Services.AddHttpClient();
    }
  }
}


Delete the static keyword from the class declaration of HttpWebAPI class:

public static class HttpWebAPI

We will use dependency injection inside our Functions class to access the database context and HttpClient objects. Therefore, add these instance variables and constructor to HttpWebAPI.cs:

private readonly HttpClient _client;
private readonly ApplicationDbContext _context;

public HttpWebAPI(IHttpClientFactory httpClientFactory,
    ApplicationDbContext context) {
    _client = httpClientFactory.CreateClient();
    _context = context;
}

Add the following method to the HttpWebAPI.cs class:

[FunctionName("GetStudents")]
public IActionResult GetStudents(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "students")] HttpRequest req,
ILogger log) {
  log.LogInformation("C# HTTP GET/posts trigger function processed a request.");

  var studentsArray = _context.Students.OrderBy(s => s.School).ToArray();

  return new OkObjectResult(studentsArray);
}

All that is left for us to find out is whether or not our API works. Hit CTRL F5 inside the Visual Studio Code.


CTRL Click with your mouse on http://localhost:7071/api/students. It should open up a browser window with the following data:


It is left up to you to complete this tutorial with POST, PUT and DELETE functionality.