An Angular 2+ .Net Core Application Consuming an Azure Machine Learning Model
Mar
19
Written by:
3/19/2017 7:18 AM

You can create an Angular 2+ application that consumes a web service created from an Azure Machine Learning experiment.
This article is part of a series of articles where we create an Azure Machine Learning experiment that predicts the price of a vehicle given parameters such as make, horsepower, and body style. It does that by creating a Model based on prices of previous vehicles. We then operationalize the model by creating a web service.
In this installment, we will create an Angular 2+ application that consumes the web service. Finally we will create a programmatic method to update the model with new data gathered from our Angular 2+ application.
The series of articles are as follows:
- An End-To-End Azure Machine Learning Studio Application – Create an Azure Machine Learning experiment and operationalize it by creating a web service and consuming it using Microsoft Excel.
- (This article) An Angular 2+ Application Consuming an Azure Machine Learning Model – Create an Angular 2+ application that consumes a web service created from an Azure Machine Learning experiment.
- Retraining an Azure Machine Learning Application – This article covers the steps needed to update the Azure Machine Learning model with new data to improve its predictions.
The Application

The Angular 2+ application, that will be covered in this article, allows users to set the values that will be passed to the Azure Machine Learning web service (that was created in An End-To-End Azure Machine Learning Studio Application).

The user can click the Price Vehicle button to call the web service.
The Predicted Price, returned by the web service, will then display.

The user can also record the actual sale price (based on the currently selected values) by entering the value and clicking the Record Price button.
The data will be stored in the local database file.

Clicking the Export Data button will produce a .csv file that can then be used to retrain the Azure Machine Learning Model, thereby improving the future predictions.
Creating The Application
If you do not already have them, install the following prerequisites:

Log into the Azure Machine Learning Studio at: https://studio.azureml.net/.

Select Web Services, then the predictive web service that we created in the previous article.

1) Click the button to copy the API Key. Save it. We will need it later. This will be what we will use as the PrimaryKey value in the appsettings.json file.
2) Click the REQUEST/RESPONSE link that will take us to the sample code page.

When the Request Response API Documentation page appears, copy the Request POST web address. Save it. We will need it later.
This will be what we will use as the BaseAddress value in the appsettings.json file.

Next, scroll down to the bottom of the page to see sample code that calls the web service.
This is what we will use at the base code for our application.
Create The .Net Core Application

Create a folder on your Microsoft Windows computer (this tutorial was created using Windows 10).
Note: Do not have any special characters in the folder name. For example, and exclamation mark (!) will break the JavaScript code.

You can type CMD and press Enter to switch to the command line (and still be in that directory).

If you have not already installed JavaScriptServices, install them by entering (and pressing Enter):
dotnet new --install Microsoft.AspNetCore.SpaTemplates::*

The screen will display indicating the templates now available.

Create the ASP.NET Core JavaScriptServices application by entering (and pressing Enter):
dotnet new angular

The application will be created.
Double-click on the *.csproj file to open it in Visual Studio 2017.

It will start restoring .Net dependencies and the node_modules (you can view the ../node_modules directory to see the items populated).
(Note: This can take 3-10 minutes or more)

Visual Studio will indicate when everything is complete.

Press Ctrl+F5 to Start Without Debugging.

The application will show.
Close the web browser for now.
Add PrimeNG

We will now install the free open source PrimeNG Angular 2 components.

Open the package.json file and add:
"font-awesome": "^4.7.0",
"primeng": "^2.0.0"
Save the file.

Open the webpack.config.vendor.js file and add:
'font-awesome/css/font-awesome.css',
'primeng/primeng',
'primeng/resources/themes/omega/theme.css',
'primeng/resources/primeng.min.css',

Also, in rules/test, add:
|gif
Save the file.

At this time, PrimeNg does not support pre-rendering so in ..\Views\Home\Index.cshtml, change:
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
to:
<app>Loading...</app>
Save the file.

Open ..\Views\Shared\_Layout.cshtml and replace all the code with the following code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - AzureMLCore</title>
<base href="/" />
<link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true" />
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink(".Net Core Angular 2 Azure Machine Learning App",
"Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
</div>
</div>
</div>
<div class="container body-content">
@RenderBody()
</div>
<hr />
@RenderSection("scripts", required: false)
</body>
</html>

We altered the webpack.config.vendor.js file (to add PrimeNg and Font Awesome) but the compiled files that it creates (that are used at runtime) are not updated by the normal build process. We have to run its configuration manually whenever we alter it.
In a command prompt, at the project root, run:
webpack --config webpack.config.vendor.js
(Note: If you don’t already have the webpack tool installed (for example you get an error when you run the code above), you’ll need to run: npm install -g webpack first)
Add The Database

Right-click on the project node, select Add then New Scaffolded Item…

Select Full Dependencies then click Add.

The Scaffolding will run.

Close the ScaffoldingReadMe.txt file that opens.
We will perform the needed changes it describes in the next steps.

Right-click on the project node and select Edit AzureMLCore.csproj.

Add:
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" />
Save and close the file.

From the menu in Visual Studio, select Tools then Connect to Database…

- Ensure that Microsoft SQL Server Database File (SqlClient) is selected for Data source (use the Change button if not)
- Enter AzureMLData.mdf for the database name and indicate that it is in a folder called Data that is under your project root (the file does not exist, it will be created by Visual Studio)
- Click OK

Click Yes to create the database.

In the Server Explorer window in Visual Studio (you can get to it from the Visual Studio menu using View then Server Explorer), the database will show.
Expand it, right-click on the Tables node and select Add New Table.

Enter the following script and click the Update button:
CREATE TABLE [dbo].[Vehicles] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[symboling] NVARCHAR (50) NULL,
[normalized-losses] NVARCHAR (50) NULL,
[make] NVARCHAR (50) NULL,
[fuel-type] NVARCHAR (50) NULL,
[aspiration] NVARCHAR (50) NULL,
[num-of-doors] NVARCHAR (50) NULL,
[body-style] NVARCHAR (50) NULL,
[drive-wheels] NVARCHAR (50) NULL,
[engine-location] NVARCHAR (50) NULL,
[wheel-base] NVARCHAR (50) NULL,
[length] NVARCHAR (50) NULL,
[width] NVARCHAR (50) NULL,
[height] NVARCHAR (50) NULL,
[curb-weight] NVARCHAR (50) NULL,
[engine-type] NVARCHAR (50) NULL,
[num-of-cylinders] NVARCHAR (50) NULL,
[engine-size] NVARCHAR (50) NULL,
[fuel-system] NVARCHAR (50) NULL,
[bore] NVARCHAR (50) NULL,
[stroke] NVARCHAR (50) NULL,
[compression-ratio] NVARCHAR (50) NULL,
[horsepower] NVARCHAR (50) NULL,
[peak-rpm] NVARCHAR (50) NULL,
[city-mpg] NVARCHAR (50) NULL,
[highway-mpg] NVARCHAR (50) NULL,
[price] NVARCHAR (50) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);

Click Update Database.

The Data Tools Operations window will indicate when the update is complete.

In the Server Explorer window, right-click on the database and select Refresh.
You will see the Vehicles table.
Always Close the database connection when done working with it to prevent locking.

In the Solution Explorer, right-click on the project node and select Manage NuGet Packages.

Search for and install: Microsoft.EntityFrameworkCore.Tools.

Open the NuGet Package Manager Console.

Enter:
Scaffold-DbContext "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\TEMP\AzureMLCore\Data\AzureMLData.mdf;Integrated Security=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
and press Enter.
(update the connection string above to point to the location of the .mdf file in the project)

The scaffolded files will appear in the Models directory.
Note: If you get DBContext cannot be found errors (red squiggly lines in the Visual Studio text editor, simply close Visual Studio and re-open it.

Rename the DataContext file and the class to AzureMLDataContext.

Next, we follow the directions at this link: ASP.NET Core - Existing Database.
We remove the OnConfiguring method.
We add the following constructor to the class:
public AzureMLDataContext(DbContextOptions<AzureMLDataContext> options) :
base(options) { }

We add the following using statements to Startup.cs:
using AzureMLCore.Models;
using Microsoft.EntityFrameworkCore;

We add the following code to the ConfigureServices section to configure the database setting:
services.AddDbContext<AzureMLDataContext>(
options => options.UseSqlServer(
Configuration.GetConnectionString("AzureMLDataDatabase")));

Add the following database setting to the appsettings.json file:
"ConnectionStrings": {
"AzureMLDataDatabase": "Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename=
C:\\Temp\\AzureMLCore\\Data\\AzureMLData.mdf;Integrated Security=True;"
},
(Note: The AzureMLDataDatabase value needs to be on a single line – see the source code for the exact setting)
Create Code To Call Azure Machine Learning Web Service
We will now create the code that will call the Azure Machine Learning web service (that was created in An End-To-End Azure Machine Learning Studio Application), passing the parameters (such as such as make, horsepower, and body style) and retrieve a response by the underlying Model in the form of a predicted price.

First, we need to create a class that will pass the settings (PrimaryKey and BaseAddress) from the app settings file to the code we will create.
Create a file called AzureMLSettings.cs using the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AzureMLCore.Models
{
public class AzureMLSettings
{
public string PrimaryKey { get; set; }
public string BaseAddress { get; set; }
}
}

Add the following code to the ConfigureServices method in the Startup.cs file:
// Get the AzureMLSettings
services.Configure<AzureMLSettings>(
Configuration.GetSection("AzureMLSettings"));

Next, add the settings to the appsettings.json file, replacing {{ API Key }} and {{ Base Address }} with the values you saved earlier:
"AzureMLSettings": {
"PrimaryKey": "{{ API Key }}",
"BaseAddress": "{{ Base Address }}"
},
The values will be retrieved and passed to the web service using the code we will create in the following two steps.

Create a file called AzureMLParameter.cs using the following code:
using System.ComponentModel.DataAnnotations;
namespace AzureMLCore.Models
{
public class AzureMLParameter
{
[Key]
public int Id { get; set; }
public string symboling { get; set; }
public string normalizedlosses { get; set; }
public string make { get; set; }
public string fueltype { get; set; }
public string aspiration { get; set; }
public string numofdoors { get; set; }
public string bodystyle { get; set; }
public string drivewheels { get; set; }
public string enginelocation { get; set; }
public string wheelbase { get; set; }
public string length { get; set; }
public string width { get; set; }
public string height { get; set; }
public string curbweight { get; set; }
public string enginetype { get; set; }
public string numofcylinders { get; set; }
public string enginesize { get; set; }
public string fuelsystem { get; set; }
public string bore { get; set; }
public string stroke { get; set; }
public string compressionratio { get; set; }
public string horsepower { get; set; }
public string peakrpm { get; set; }
public string citympg { get; set; }
public string highwaympg { get; set; }
public string price { get; set; }
public string Scoredlabels { get; set; }
}
}

Finally, to we will create the controller class, that will be called by the Angular code, that will call the web service.
Create a file called AzureMLParameterController.cs using the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AzureMLCore.Models;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Net.Http;
using Microsoft.Extensions.Options;
using System.Text;
namespace AngularAzureMLApp.Controllers
{
[Produces("application/json")]
[Route("api/AzureMLParameter")]
public class AzureMLParameterController : Controller
{
// Global values to hold the PrimaryKey and BaseAddress
private string _PrimaryKey;
private string _BaseAddress;
// This is the default contructor for the class
// We will inject the AzureMLSettings when the class
// is instantiated
public AzureMLParameterController(
IOptions<AzureMLSettings> AzureMLSettings)
{
// Set the values for PrimaryKey and BaseAddress
_PrimaryKey = AzureMLSettings.Value.PrimaryKey;
_BaseAddress = AzureMLSettings.Value.BaseAddress;
}
#region public IHttpActionResult Post(AzureMLParameter paramAzureMLParameter)
// POST: odata/AzureMLParameter
[HttpPost]
public async Task<IActionResult> Post([FromBody] AzureMLParameter paramAzureMLParameter)
{
// Call the web service
string ScoredLabels = await InvokeRequestResponseService(paramAzureMLParameter);
paramAzureMLParameter.Scoredlabels = ScoredLabels;
// Return the result
return Ok(paramAzureMLParameter);
}
#endregion
// Helpers
#region async Task<string> InvokeRequestResponseService(AzureMLParameter objAzureMLParameter)
async Task<string> InvokeRequestResponseService(AzureMLParameter objAzureMLParameter)
{
string strResponse = "";
try
{
// Get the Global values for PrimaryKey and BaseAddress
string PrimaryKey = _PrimaryKey;
string BaseAddress = _BaseAddress;
using (var client = new HttpClient())
{
// We will create a request to the web service
// Note: The only columns that are actually being used by the Model and that need to be set:
// highwaympg, citympg, horsepower, fuelsystem, enginesize, numofcylinders
// enginetype, bodystyle, make, curbweight, wheelbase, numofdoors, fueltype
var scoreRequest = new
{
Inputs = new Dictionary<string, StringTable>() {
{
"input1",
new StringTable()
{
ColumnNames = new string[] {
"symboling", "normalized-losses","make","fuel-type","aspiration",
"num-of-doors","body-style","drive-wheels","engine-location",
"wheel-base","length","width", "height","curb-weight","engine-type",
"num-of-cylinders","engine-size","fuel-system","bore","stroke",
"compression-ratio","horsepower","peak-rpm","city-mpg","highway-mpg","price"},
Values = new string[,] {
{
objAzureMLParameter.symboling,
objAzureMLParameter.normalizedlosses,
objAzureMLParameter.make,
objAzureMLParameter.fueltype,
objAzureMLParameter.aspiration,
objAzureMLParameter.numofdoors,
objAzureMLParameter.bodystyle,
objAzureMLParameter.drivewheels,
objAzureMLParameter.enginelocation,
objAzureMLParameter.wheelbase,
objAzureMLParameter.length,
objAzureMLParameter.width,
objAzureMLParameter.height,
objAzureMLParameter.curbweight,
objAzureMLParameter.enginetype,
objAzureMLParameter.numofcylinders,
objAzureMLParameter.enginesize,
objAzureMLParameter.fuelsystem,
objAzureMLParameter.bore,
objAzureMLParameter.stroke,
objAzureMLParameter.compressionratio,
objAzureMLParameter.horsepower,
objAzureMLParameter.peakrpm,
objAzureMLParameter.citympg,
objAzureMLParameter.highwaympg,
objAzureMLParameter.price
}
}
}
},
},
GlobalParameters = new Dictionary<string, string>()
{
}
};
// Create an authorization value
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", PrimaryKey);
// Call the web service
HttpResponseMessage response =
await client.PostAsync(new Uri(BaseAddress),
new StringContent(JsonConvert.SerializeObject(scoreRequest),
Encoding.UTF8, "application/json"));
if (response.IsSuccessStatusCode)
{
// The call was a success -- get the response
string result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
Rootobject obj = JsonConvert.DeserializeObject<Rootobject>(result);
// The predicted price is in the last element
int intLastElement = (obj.Results.output1.value.Values[0].Count() - 1);
var Price = obj.Results.output1.value.Values[0][intLastElement];
// Convert the value to a currency string
strResponse = Convert.ToDecimal(Price).ToString("C");
}
else
{
// The call returned an error
var strContent = await response.Content.ReadAsStringAsync();
strResponse = string.Format("The request failed with status code: {0}",
response.StatusCode);
// Add the headers - they include the request ID and the timestamp,
// which are useful for debugging the failure
strResponse = strResponse + " " + response.Headers.ToString();
strResponse = strResponse + " " + strContent;
}
}
}
catch (Exception ex)
{
return ex.Message;
}
return strResponse;
}
#endregion
#region public class StringTable
public class StringTable
{
public string[] ColumnNames { get; set; }
public string[,] Values { get; set; }
}
#endregion
#region JSON Result Classes
public class Rootobject
{
public Results Results { get; set; }
}
public class Results
{
public Output1 output1 { get; set; }
}
public class Output1
{
public string type { get; set; }
public Value value { get; set; }
}
public class Value
{
public string[] ColumnNames { get; set; }
public string[] ColumnTypes { get; set; }
public string[][] Values { get; set; }
}
#endregion
}
}
Create The Angular Application
Let’s build the entire Angular application from scratch.
Highlight all the files under: …\ClientApp\app\

…and delete them.

The entry point for the custom code for the application is in app.module.ts.
Create the file using the following code:
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { UniversalModule } from 'angular2-universal';
import { AppComponent } from './app.component'
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';
import {
InputTextModule,
DropdownModule,
ButtonModule,
FieldsetModule,
TreeModule,
TreeNode,
SelectItem,
TabMenuModule,
MenuItem,
TabViewModule,
PanelModule,
InputSwitchModule,
PasswordModule
} from 'primeng/primeng';
import { PriceVehicleComponent } from './priceVehicle/priceVehicle.component';
import { PriceVehicleService } from './priceVehicle/priceVehicle.service';
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
PriceVehicleComponent,
],
imports: [
UniversalModule, // Must be first import.
BrowserModule,
HttpModule,
FormsModule,
InputTextModule,
TreeModule,
DropdownModule,
ButtonModule,
FieldsetModule,
TabMenuModule,
TabViewModule,
PanelModule,
InputSwitchModule,
PasswordModule
],
providers: [
PriceVehicleService
]
})
export class AppModule {
}
(note: you will see red swiggly lines in the Visual Studio editor because there are files being referenced that we have not created yet)

app.module, in it’s bootstrap setting, it indicates that AppComponent is the first component to be loaded.
To implement it, create the file: app.component.ts using the following code:
import { Component } from '@angular/core';
@Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
}

app.component.ts specifies app.component.html for templateUrl and app.component.css for styleUrls.
Create those files and implement them using the following code:
app.component.html:
<priceVehicle-form>Loading...</priceVehicle-form>
app.component.css:
@media (max-width: 767px) {
/* On small screens, the nav menu spans the full width of the screen.
Leave a space for it. */
.body-content {
padding-top: 50px;
}
}

The primary code for the application will be placed in the priceVehicle folder.
Create It.

We will create an interface that will allow us to communicate with the AzureMLParameterController.cs file using a strongly typed class.
Create a file: azureMLParameter.ts using the following code:
/* Defines the AzureMLParameter entity */
export interface IAzureMLParameter {
id: number;
symboling: string;
normalizedlosses: string;
make: string;
fueltype: string;
aspiration: string;
numofdoors: string;
bodystyle: string;
drivewheels: string;
enginelocation: string;
wheelbase: string;
length: string;
width: string;
height: string;
curbweight: string;
enginetype: string;
numofcylinders: string;
enginesize: string;
fuelsystem: string;
bore: string;
stroke: string;
compressionratio: string;
horsepower: string;
peakrpm: string;
citympg: string;
highwaympg: string;
price: string; // price we submit
scoredlabels: string; // prediction we get back
}

We will create a service that will communicate with the C# controller code, both to get price predictions and to save actual prices to the database.
Create a file: priceVehicle.service.ts using the following code:
import { Injectable } from '@angular/core';
import { Http, Response, RequestOptions, Request, RequestMethod, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { IAzureMLParameter } from './azureMLParameter';
@Injectable()
export class PriceVehicleService {
constructor(private _http: Http) { }
priceVehicle(AzureMLParameter: IAzureMLParameter): Observable<IAzureMLParameter> {
var _Url = 'api/AzureMLParameter';
AzureMLParameter.price = "0";
// This is a Post so we have to pass Headers
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
// Make the Angular 2 Post
// passing the AzureMLParameter
return this._http.post(_Url, JSON.stringify(AzureMLParameter), options)
.map((response: Response) => <IAzureMLParameter>response.json())
.catch(this.handleError);
}
recordPrice(AzureMLParameter: IAzureMLParameter): Observable<IAzureMLParameter> {
var _Url = 'api/Vehicles';
// This is a Post so we have to pass Headers
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
// Make the Angular 2 Post
// passing the AzureMLParameter
return this._http.post(_Url, JSON.stringify(AzureMLParameter), options)
.map((response: Response) => <IAzureMLParameter>response.json())
.catch(this.handleError);
}
// Utility
private handleError(error: Response) {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
(Note: we have implemented code to save recorded prices, but we have not added the server-side C# code yet)

Next we will add the actual markup for the application.
Create a file: priceVehicle.component.html using the following code:
<div style="width:800px">
<br /><br /><br />
<div>
<span style="background-color: #FFFF00">{{errorMessage}}</span>
</div>
<h3 class="first">Price A Vehicle</h3>
<button pButton type="button" label="Export Data" (click)="getVehicles()" class="ui-button-secondary"></button>
<div class="ui-grid ui-grid-responsive ui-grid-pad">
<div class="ui-grid-row">
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Make:
</div>
<div class="ui-grid-col-8">
<p-dropdown id="Make"
[options]="makeDropdown"
[(ngModel)]="AzureMLParameter.make"
[style]="{'width':'150px'}"></p-dropdown>
</div>
</div>
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Fuel type:
</div>
<div class="ui-grid-col-8">
<p-dropdown id="FuelType"
[options]="fueltypeDropdown"
[(ngModel)]="AzureMLParameter.fueltype"
[style]="{'width':'150px'}"></p-dropdown>
</div>
</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Num of doors:
</div>
<div class="ui-grid-col-8">
<p-dropdown id="NumOfDoors"
[options]="numofdoorsDropdown"
[(ngModel)]="AzureMLParameter.numofdoors"
[style]="{'width':'150px'}"></p-dropdown>
</div>
</div>
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Body style:
</div>
<div class="ui-grid-col-8">
<p-dropdown id="BodyStyle"
[options]="bodystyleDropdown"
[(ngModel)]="AzureMLParameter.bodystyle"
[style]="{'width':'150px'}"></p-dropdown>
</div>
</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Wheel base:
</div>
<div class="ui-grid-col-8">
<input type="text" id="WheelBase" pInputText [(ngModel)]="AzureMLParameter.wheelbase" />
</div>
</div>
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Curb weight:
</div>
<div class="ui-grid-col-8">
<input type="text" id="CurbWeight" pInputText [(ngModel)]="AzureMLParameter.curbweight" />
</div>
</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Engine type:
</div>
<div class="ui-grid-col-8">
<p-dropdown id="EngineType"
[options]="enginetypeDropdown"
[(ngModel)]="AzureMLParameter.enginetype"
[style]="{'width':'150px'}"></p-dropdown>
</div>
</div>
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Num of cylinders:
</div>
<div class="ui-grid-col-8">
<p-dropdown id="NumOfCylinders"
[options]="numofcylindersDropdown"
[(ngModel)]="AzureMLParameter.numofcylinders"
[style]="{'width':'150px'}"></p-dropdown>
</div>
</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Engine size:
</div>
<div class="ui-grid-col-8">
<input type="text" id="EngineSize" pInputText [(ngModel)]="AzureMLParameter.enginesize" />
</div>
</div>
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Fuel system:
</div>
<div class="ui-grid-col-8">
<p-dropdown id="FuelSystem"
[options]="fuelsystemDropdown"
[(ngModel)]="AzureMLParameter.fuelsystem"
[style]="{'width':'150px'}"></p-dropdown>
</div>
</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Horsepower:
</div>
<div class="ui-grid-col-8">
<input type="text" id="Horsepower" pInputText [(ngModel)]="AzureMLParameter.horsepower" />
</div>
</div>
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
City mpg:
</div>
<div class="ui-grid-col-8">
<input type="text" id="CityMpg" pInputText [(ngModel)]="AzureMLParameter.citympg" />
</div>
</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
Highway mpg:
</div>
<div class="ui-grid-col-8">
<input type="text" id="HighwayMpg" pInputText [(ngModel)]="AzureMLParameter.highwaympg" />
</div>
</div>
</div>
</div>
<div class="ui-grid ui-grid-responsive ui-grid-pad">
<div class="ui-grid-row">
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
<button pButton type="button" label="Price Vehicle" (click)="priceVehicle()"></button>
</div>
<div class="ui-grid-col-8">
<p>Predicted Price: <b>{{AzureMLParameter.scoredlabels}}</b></p>
</div>
</div>
<div class="ui-grid-col-6">
<div class="ui-grid-col-4">
<button pButton type="button" label="Record Price" (click)="recordPrice()"></button>
</div>
<div class="ui-grid-col-8">
<input type="text" id="Price" pInputText [(ngModel)]="AzureMLParameter.price" />
</div>
</div>
</div>
</div>
</div>

Finally, add the primary custom code for the application by creating a file called priceVehicle.component.ts using the following code:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import {
SelectItem,
InputTextModule,
ButtonModule,
DropdownModule
}
from 'primeng/primeng';
import { IAzureMLParameter } from './azureMLParameter';
import { PriceVehicleService } from './priceVehicle.service';
@Component({
selector: 'priceVehicle-form',
templateUrl: './priceVehicle.component.html'
})
export class PriceVehicleComponent implements OnInit {
errorMessage: string;
AzureMLParameter: IAzureMLParameter;
makeDropdown: SelectItem[] = [];
fueltypeDropdown: SelectItem[] = [];
numofdoorsDropdown: SelectItem[] = [];
bodystyleDropdown: SelectItem[] = [];
enginetypeDropdown: SelectItem[] = [];
numofcylindersDropdown: SelectItem[] = [];
fuelsystemDropdown: SelectItem[] = [];
// Register the service
constructor(private _PriceVehicleService: PriceVehicleService) { }
ngOnInit(): void {
// Set default values for vehicle parameters
this.setDefaultValues();
// Fill the dropdowns
this.fillDropdowns();
}
priceVehicle() {
this.errorMessage = "";
// Get the predicted price for a vehicle
// Call the service
this._PriceVehicleService.priceVehicle(this.AzureMLParameter)
.subscribe(
AzureMLParameter => {
this.AzureMLParameter = AzureMLParameter
},
error => this.errorMessage = <any>error);
}
recordPrice() {
this.errorMessage = "";
// Save the price of a vehicle to the database
// Call the service
this._PriceVehicleService.recordPrice(this.AzureMLParameter)
.subscribe(
AzureMLParameter => {
alert('Recorded: ' + AzureMLParameter.id);
},
error => this.errorMessage = <any>error);
}
getVehicles() {
window.location.href = 'Home/DownloadCSV';
}
// Utility
setDefaultValues() {
// Note: The only columns that are actually being used by the Model and that need to be set:
// highwaympg, citympg, horsepower, fuelsystem, enginesize, numofcylinders
// enginetype, bodystyle, make, curbweight, wheelbase, numofdoors, fueltype
let NewAzureMLParameter: IAzureMLParameter = {
id: 0,
symboling: "0",
normalizedlosses: "1",
make: "toyota",
fueltype: "gas",
aspiration: " ",
numofdoors: "four",
bodystyle: "sedan",
drivewheels: " ",
enginelocation: " ",
wheelbase: "99.4",
length: "0",
width: "0",
height: "0",
curbweight: "2824",
enginetype: "ohc",
numofcylinders: "five",
enginesize: "136",
fuelsystem: "mpfi",
bore: "0",
stroke: "0",
compressionratio: "0",
horsepower: "115",
peakrpm: "0",
citympg: "18",
highwaympg: "22",
price: "0",
scoredlabels: " "
}
this.AzureMLParameter = NewAzureMLParameter;
}
fillDropdowns() {
// makeDropdown
this.makeDropdown.push({ label: 'toyota', value: 'toyota' });
this.makeDropdown.push({ label: 'nissan', value: 'nissan' });
this.makeDropdown.push({ label: 'mazda', value: 'mazda' });
this.makeDropdown.push({ label: 'mitsubishi', value: 'mitsubishi' });
this.makeDropdown.push({ label: 'honda', value: 'honda' });
this.makeDropdown.push({ label: 'volkswagen', value: 'volkswagen' });
this.makeDropdown.push({ label: 'subaru', value: 'subaru' });
this.makeDropdown.push({ label: 'volvo', value: 'volvo' });
this.makeDropdown.push({ label: 'peugot', value: 'peugot' });
this.makeDropdown.push({ label: 'dodge', value: 'dodge' });
// fueltypeDropdown
this.fueltypeDropdown.push({ label: 'gas', value: 'gas' });
this.fueltypeDropdown.push({ label: 'diesel', value: 'diesel' });
// numofdoorsDropdown
this.numofdoorsDropdown.push({ label: 'four', value: 'four' });
this.numofdoorsDropdown.push({ label: 'two', value: 'two' });
// bodystyleDropdown
this.bodystyleDropdown.push({ label: 'sedan', value: 'sedan' });
this.bodystyleDropdown.push({ label: 'hatchback', value: 'hatchback' });
this.bodystyleDropdown.push({ label: 'wagon', value: 'wagon' });
this.bodystyleDropdown.push({ label: 'hardtop', value: 'hardtop' });
this.bodystyleDropdown.push({ label: 'convertible', value: 'convertible' });
// enginetypeDropdown
this.enginetypeDropdown.push({ label: 'ohc', value: 'ohc' });
this.enginetypeDropdown.push({ label: 'ohcf', value: 'ohcf' });
this.enginetypeDropdown.push({ label: 'ohcv', value: 'ohcv' });
this.enginetypeDropdown.push({ label: 'dohc', value: 'dohc' });
this.enginetypeDropdown.push({ label: 'l', value: 'l' });
this.enginetypeDropdown.push({ label: 'rotor', value: 'rotor' });
this.enginetypeDropdown.push({ label: 'dohcv', value: 'dohcv' });
// numofcylindersDropdown
this.numofcylindersDropdown.push({ label: 'four', value: 'four' });
this.numofcylindersDropdown.push({ label: 'six', value: 'six' });
this.numofcylindersDropdown.push({ label: 'five', value: 'five' });
this.numofcylindersDropdown.push({ label: 'eight', value: 'eight' });
this.numofcylindersDropdown.push({ label: 'two', value: 'two' });
this.numofcylindersDropdown.push({ label: 'three', value: 'three' });
this.numofcylindersDropdown.push({ label: 'twelve', value: 'twelve' });
// fuelsystemDropdown
this.fuelsystemDropdown.push({ label: 'mpfi', value: 'mpfi' });
this.fuelsystemDropdown.push({ label: '2bbl', value: '2bbl' });
this.fuelsystemDropdown.push({ label: 'idi', value: 'idi' });
this.fuelsystemDropdown.push({ label: '1bbl', value: '1bbl' });
this.fuelsystemDropdown.push({ label: 'spdi', value: 'spdi' });
this.fuelsystemDropdown.push({ label: '4bbl', value: '4bbl' });
this.fuelsystemDropdown.push({ label: 'mfi', value: 'mfi' });
this.fuelsystemDropdown.push({ label: 'spfi', value: 'spfi' });
}
}

Run the application.

The application will show, and we can click the Price Vehicle button to see the predicted price for the options selected on the form.
Saving Data

To save data, we need to add a server-side method.
Create a file at: ..\Controllers\VehiclesController.cs using the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AzureMLCore.Models;
namespace AzureMLCore.Controllers
{
[Produces("application/json")]
[Route("api/Vehicles")]
public class VehiclesController : Controller
{
private readonly AzureMLDataContext _context;
public VehiclesController(AzureMLDataContext context)
{
_context = context;
}
// POST: api/Vehicles
[HttpPost]
public IActionResult PostVehicles([FromBody] Vehicles vehicles)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Vehicles.Add(vehicles);
_context.SaveChanges();
return CreatedAtAction("GetVehicles", new { id = vehicles.Id }, vehicles);
}
}
}

Rebuild the application and re-launch or refresh it in the web browser.
We can now enter a recorded price and save it to the database.
Viewing Data

We can export the recorded prices.
We will use this data in the next article to retrain the Model used by the web service to make price predictions.

Open the HomeController.cs file and add the following code to the class:
private readonly Models.AzureMLDataContext _context;
public HomeController(Models.AzureMLDataContext context)
{
_context = context;
}
public FileContentResult DownloadCSV()
{
// Break up array so the code formats properly for the blog post article
string strArray1 = "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},";
string strArray2 = "{11},{12},{13},{14},{15},{16},{17},{18},{19},";
string strArray3 = "{20},{21},{22},{23},{24},{25}\n";
string strFullArray = strArray1 + strArray2 + strArray3;
// This returns all vehicles in the database
var result = (from vehicle in _context.Vehicles
select new Models.AzureMLParameter
{
Id = vehicle.Id,
symboling = vehicle.Symboling,
normalizedlosses = vehicle.NormalizedLosses,
make = vehicle.Make,
fueltype = vehicle.FuelType,
aspiration = vehicle.Aspiration,
numofdoors = vehicle.NumOfDoors,
bodystyle = vehicle.BodyStyle,
drivewheels = vehicle.DriveWheels,
enginelocation = vehicle.EngineLocation,
wheelbase = vehicle.WheelBase,
length = vehicle.Length,
width = vehicle.Width,
height = vehicle.Height,
curbweight = vehicle.CurbWeight,
enginetype = vehicle.EngineType,
numofcylinders = vehicle.NumOfCylinders,
enginesize = vehicle.EngineSize,
fuelsystem = vehicle.FuelSystem,
bore = vehicle.Bore,
stroke = vehicle.Stroke,
compressionratio = vehicle.CompressionRatio,
horsepower = vehicle.Horsepower,
peakrpm = vehicle.PeakRpm,
citympg = vehicle.CityMpg,
highwaympg = vehicle.HighwayMpg,
price = vehicle.Price
}).ToList();
// Write headers
string csv = string.Format(strFullArray,
"symboling",
"normalized-losses",
"make",
"fuel-type",
"aspiration",
"num-of-doors",
"body-style",
"drive-wheels",
"engine-location",
"wheel-base",
"length",
"width",
"height",
"curb-weight",
"engine-type",
"num-of-cylinders",
"engine-size",
"fuel-system",
"bore",
"stroke",
"compression-ratio",
"horsepower",
"peak-rpm",
"city-mpg",
"highway-mpg",
"price");
// Write data
csv = csv + string.Concat(from vehicle in result
select string.Format(strFullArray,
vehicle.symboling,
vehicle.normalizedlosses,
vehicle.make,
vehicle.fueltype,
vehicle.aspiration,
vehicle.numofdoors,
vehicle.bodystyle,
vehicle.drivewheels,
vehicle.enginelocation,
vehicle.wheelbase,
vehicle.length,
vehicle.width,
vehicle.height,
vehicle.curbweight,
vehicle.enginetype,
vehicle.numofcylinders,
vehicle.enginesize,
vehicle.fuelsystem,
vehicle.bore,
vehicle.stroke,
vehicle.compressionratio,
vehicle.horsepower,
vehicle.peakrpm,
vehicle.citympg,
vehicle.highwaympg,
vehicle.price
));
return File(new System.Text.UTF8Encoding().GetBytes(csv), "text/csv", "vehicles.csv");
}
Links
Download
You can download the code from the Download page
4 comment(s) so far...
Re: An Angular 2+ .Net Core Application Consuming an Azure Machine Learning Model
Hi,
Have been having serious issues with creating a database as regards adding new scaffolded "MVC full dependencies" is not part of the available option in my visual studio 2017. though that's what I have seen on the book I purchased and other videos.
this is not allowing my program to run at all. please can you be of help?
By David Oladapo on
11/10/2017 2:12 AM
|
Re: An Angular 2+ .Net Core Application Consuming an Azure Machine Learning Model
@David Oladapo - Can you please make a post to the forums on this site (aihelpwebsite.com/Forums/forumid/1/scope/threads)? I Need to know exactly what step is not working for you. Meaning, what does the article instruct you to do but does not work? Thanks!
By Administrator on
11/10/2017 8:56 AM
|
Re: An Angular 2+ .Net Core Application Consuming an Azure Machine Learning Model
Hi, 'Add Database' step cannot e completed as adding scaffolding item feature is no longer available in visual studio 2017. How can I continue to develop the above application?
Thanks in advance
By Indeevari on
11/25/2017 1:47 AM
|
Re: An Angular 2+ .Net Core Application Consuming an Azure Machine Learning Model
@Indeevari - Ensure you have upgraded to the latest Visual Studio 2017. You should no longer need the scaffolding steps. However, all the other steps including Connect to Database should still work. See: docs.microsoft.com/en-us/visualstudio/data-tools/add-new-connections
By Administrator on
11/25/2017 6:57 AM
|