Monday, December 20, 2021

Generate charts with Google DataTable .NET Wrapper from ASP.NET 6.0 Razor Pages App

The scenario that this article addresses is a situation whereby there is server-side generated data that needs to be displayed on a web page as a chart. The API used for generating the chart is the freely available Google Charts JavaScript-based API. The Google DataTable .NET Wrapper is used to create a lightweight representation of the google.visualization.DataTable object directly in Microsoft.NET. The wrapper allows for the creation of the appropriate JSON which is easily ingested by the Google Chart Tools JavaScript library.

I will show you how to generate six types of charts to display dynamically generated data. The source of data will be the well known Northwind database running in a Docker container. I will work with the ASP.NET Razor Pages template (AKA Web App).

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

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 ChartRazorGoogleWrapper:

dotnet new razor -f net6.0 -o ChartRazorGoogleWrapper

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

cd ChartRazorGoogleWrapper 

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


Also, install the Google DataTable .NET Wrapper Nuget package:

dotnet add package Google.DataTable.Net.Wrapper

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 only the Orders 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 Orders

This creates a NW folder in your project with entity Orders and 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

In the Pages folder, add two files ChartData.cshtml and ChartData.cshtml.cs.

Content of ChartData.cshtml is:

@page
@model ChartDataModel

Content of ChartData.cshtml.cs is:

using ChartRazorGoogleWrapper.NW;
using Google.DataTable.Net.Wrapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;

namespace ChartRazorGoogleWrapper.Pages;

public class ChartDataModel : PageModel {
   private readonly ILogger<ChartDataModel> _logger;
   private readonly NorthwindContext _northwindContext;

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

   public async Task<IActionResult> OnGet() {
     var data = await _northwindContext.Orders
     .GroupBy(_ => _.ShipCity)
     .Select(g => new {
         Name = g.Key,
         Count = g.Count()
     })
     .OrderByDescending(cp => cp.Count)
     .ToListAsync();

     //let's instantiate the DataTable.
     var dt = new Google.DataTable.Net.Wrapper.DataTable();
     dt.AddColumn(new Column(ColumnType.String, "Name", "Name"));
     dt.AddColumn(new Column(ColumnType.Number, "Count", "Count"));

     foreach (var item in data) {
         Row r = dt.NewRow();
         r.AddCellRange(new Cell[] {
             new Cell(item.Name),
             new Cell(item.Count)
         });
         dt.AddRow(r);
     }

     //Let's create a Json string as expected by the Google Charts API.
     return Content(dt.GetJson());
   }
}

The above code in ChartData.cshtml.cs returns a JSON representation of  Google.DataTable.Net.Wrapper.DataTable. It contains data from the Northwind database representing the number of orders by city.

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/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 Pages/Index.cshtml with the following code:

@page
@model IndexModel

<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>
<div id="pie_chart_3d_div"></div>


<script type="text/javascript">
    google.charts.load('current', {
        packages: ['corechart', 'bar']
    });

    google.setOnLoadCallback(drawChart);

    function drawChart() {
        var jsonData = $.ajax({
            url: '/ChartData',
            dataType: "json",
            async: false
        }).responseText;

        PopulationChart(jsonData, "column-chart");
        PopulationChart(jsonData, "line-chart");
        PopulationChart(jsonData, "pie-chart");
        PopulationChart(jsonData, "area-chart");
        PopulationChart(jsonData, "bar-chart");
        PopulationChart(jsonData, "pie-chart-3d");
    }

    function PopulationChart(jsonData, chart_type) {
        // Create our data table out of JSON data loaded from server.
        var data = new google.visualization.DataTable(jsonData);
        var chart;
        var options = { title: 'Orders by city' };

        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;
            case "pie-chart-3d":
                options.is3D = true;
                chart = new google.visualization.PieChart(document.getElementById('pie_chart_3d_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 six charts, namely: column, line, pie, area, bar and pie 3D charts.




Conclusion

This shows you how the Google Chart Tools JavaScript library makes it much easier to generate charts from an ASP.NET Razor application.


No comments:

Post a Comment