Client-side navigation. For example, click Counter then Back to return here.
Angular CLI integration. In development mode, there's no need to run ng serve. It runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.
Efficient production builds. In production mode, development-time features are disabled, and your dotnet publish configuration automatically invokes ng build to produce minified, ahead-of-time compiled JavaScript files.
The ClientApp subdirectory is a standard Angular CLI application. If you open a command prompt in that directory, you can run any ng command (e.g., ng test), or use npm to install extra packages into it.
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/app/home/home.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
})
export class HomeComponent {
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/app/nav-menu/nav-menu.component.css
================================================
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/app/nav-menu/nav-menu.component.html
================================================
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/app/nav-menu/nav-menu.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent {
isExpanded = false;
collapse() {
this.isExpanded = false;
}
toggle() {
this.isExpanded = !this.isExpanded;
}
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/assets/.gitkeep
================================================
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/index.html
================================================
HealthCheckLoading...
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
export function getBaseUrl() {
return document.getElementsByTagName('base')[0].href;
}
const providers = [
{ provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic(providers).bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/styles.css
================================================
/* You can add global styles to this file, and also import other style files */
/* Provide sufficient contrast against white background */
a {
color: #0366d6;
}
code {
color: #e01a76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/tsconfig.app.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/tsconfig.server.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs"
},
"angularCompilerOptions": {
"entryModule": "app/app.server.module#AppServerModule"
}
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/tsconfig.spec.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/src/tslint.json
================================================
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"module": "esnext",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/tslint.json
================================================
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"no-output-on-prefix": true,
"no-inputs-metadata-property": true,
"no-outputs-metadata-property": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-output-rename": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}
================================================
FILE: Chapter_01/HealthCheck/ClientApp/update-npm.bat
================================================
cd %~dp0
npm update
================================================
FILE: Chapter_01/HealthCheck/Controllers/WeatherForecastController.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace HealthCheck.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger _logger;
public WeatherForecastController(ILogger logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}
================================================
FILE: Chapter_01/HealthCheck/HealthCheck.csproj
================================================
netcoreapp3.1trueLatestfalseClientApp\$(DefaultItemExcludes);$(SpaRoot)node_modules\**false%(DistFiles.Identity)PreserveNewesttrue
================================================
FILE: Chapter_01/HealthCheck/Pages/Error.cshtml
================================================
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
Error.
An error occurred while processing your request.
@if (Model.ShowRequestId)
{
Request ID:@Model.RequestId
}
Development Mode
Swapping to the Development environment displays detailed information about the error that occurred.
The Development environment shouldn't be enabled for deployed applications.
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
and restarting the app.
================================================
FILE: Chapter_01/HealthCheck/Pages/Error.cshtml.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace HealthCheck.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
private readonly ILogger _logger;
public ErrorModel(ILogger logger)
{
_logger = logger;
}
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
================================================
FILE: Chapter_01/HealthCheck/Pages/_ViewImports.cshtml
================================================
@using HealthCheck
@namespace HealthCheck.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
================================================
FILE: Chapter_01/HealthCheck/Program.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace HealthCheck
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
}
}
================================================
FILE: Chapter_01/HealthCheck/Startup.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace HealthCheck
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
================================================
FILE: Chapter_01/HealthCheck/WeatherForecast.cs
================================================
using System;
namespace HealthCheck
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}
================================================
FILE: Chapter_01/HealthCheck/appsettings.Development.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
================================================
FILE: Chapter_01/HealthCheck/appsettings.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
================================================
FILE: Chapter_01.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29102.190
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthCheck", "Chapter_01\HealthCheck\HealthCheck.csproj", "{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B140A870-A419-487C-A4D5-D1949CCFB411}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B140A870-A419-487C-A4D5-D1949CCFB411}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B140A870-A419-487C-A4D5-D1949CCFB411}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B140A870-A419-487C-A4D5-D1949CCFB411}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {922DA531-077E-4848-833C-D0F1791E34E4}
EndGlobalSection
EndGlobal
================================================
FILE: Chapter_02/HealthCheck/.gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
bin/
Bin/
obj/
Obj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
orleans.codegen.cs
/node_modules
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
================================================
FILE: Chapter_02/HealthCheck/ClientApp/.editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: Chapter_02/HealthCheck/ClientApp/.gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
================================================
FILE: Chapter_02/HealthCheck/ClientApp/README.md
================================================
# HealthCheck
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: Chapter_02/HealthCheck/ClientApp/angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"HealthCheck": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"progress": true,
"extractCss": true,
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": ["src/assets"],
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "HealthCheck:build"
},
"configurations": {
"production": {
"browserTarget": "HealthCheck:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "HealthCheck:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [ "src/styles.css" ],
"scripts": [],
"assets": ["src/assets"]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["src/tsconfig.app.json", "src/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist-server",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.server.json"
},
"configurations": {
"dev": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": true
},
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false
}
}
}
}
},
"HealthCheck-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "HealthCheck:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": ["**/node_modules/**"]
}
}
}
}
},
"defaultProject": "HealthCheck"
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/browserslist
================================================
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
# IE 9-11
================================================
FILE: Chapter_02/HealthCheck/ClientApp/e2e/protractor.conf.js
================================================
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require("jasmine-spec-reporter");
exports.config = {
allScriptsTimeout: 11000,
specs: ["./src/**/*.e2e-spec.ts"],
capabilities: {
browserName: "chrome"
},
directConnect: true,
baseUrl: "http://localhost:4200/",
framework: "jasmine",
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require("ts-node").register({
project: require("path").join(__dirname, "./tsconfig.e2e.json")
});
jasmine
.getEnv()
.addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
================================================
FILE: Chapter_02/HealthCheck/ClientApp/e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getMainHeading()).toEqual('Hello, world!');
});
});
================================================
FILE: Chapter_02/HealthCheck/ClientApp/e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getMainHeading() {
return element(by.css('app-root h1')).getText();
}
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/e2e/tsconfig.e2e.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/package.json
================================================
{
"name": "healthcheck",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "echo Starting... && ng serve",
"build": "ng build",
"build:ssr": "ng run HealthCheck:server:dev",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "9.0.0",
"@angular/common": "9.0.0",
"@angular/compiler": "9.0.0",
"@angular/core": "9.0.0",
"@angular/forms": "9.0.0",
"@angular/platform-browser": "9.0.0",
"@angular/platform-browser-dynamic": "9.0.0",
"@angular/platform-server": "9.0.0",
"@angular/router": "9.0.0",
"@nguniversal/module-map-ngfactory-loader": "9.0.0-next.9",
"aspnet-prerendering": "3.0.1",
"bootstrap": "4.4.1",
"core-js": "3.6.1",
"jquery": "3.5.1",
"oidc-client": "1.9.1",
"popper.js": "1.16.0",
"rxjs": "6.5.4",
"zone.js": "0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.900.0",
"@angular/cli": "9.0.0",
"@angular/compiler-cli": "9.0.0",
"@angular/language-service": "9.0.0",
"@types/jasmine": "3.5.0",
"@types/jasminewd2": "2.0.8",
"@types/node": "13.1.1",
"codelyzer": "5.2.1",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "4.4.1",
"karma-chrome-launcher": "3.1.0",
"karma-coverage-istanbul-reporter": "2.1.1",
"karma-jasmine": "2.0.1",
"karma-jasmine-html-reporter": "1.5.1",
"typescript": "3.7.5"
},
"optionalDependencies": {
"node-sass": "4.13.0",
"protractor": "5.4.2",
"ts-node": "5.0.1",
"tslint": "5.20.1"
}
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/app/app.component.html
================================================
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
title = 'app';
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/app/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { HomeComponent } from './home/home.component';
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
HomeComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
HttpClientModule,
FormsModule,
RouterModule.forRoot([
{ path: '', component: HomeComponent, pathMatch: 'full' }
])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/app/app.server.module.ts
================================================
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { AppComponent } from './app.component';
import { AppModule } from './app.module';
@NgModule({
imports: [AppModule, ServerModule, ModuleMapLoaderModule],
bootstrap: [AppComponent]
})
export class AppServerModule { }
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/app/home/home.component.html
================================================
Hello, world!
Welcome to your new single-page application, built with:
Client-side navigation. For example, click Counter then Back to return here.
Angular CLI integration. In development mode, there's no need to run ng serve. It runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.
Efficient production builds. In production mode, development-time features are disabled, and your dotnet publish configuration automatically invokes ng build to produce minified, ahead-of-time compiled JavaScript files.
The ClientApp subdirectory is a standard Angular CLI application. If you open a command prompt in that directory, you can run any ng command (e.g., ng test), or use npm to install extra packages into it.
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/app/home/home.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
})
export class HomeComponent {
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/app/nav-menu/nav-menu.component.css
================================================
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/app/nav-menu/nav-menu.component.html
================================================
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/app/nav-menu/nav-menu.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent {
isExpanded = false;
collapse() {
this.isExpanded = false;
}
toggle() {
this.isExpanded = !this.isExpanded;
}
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/assets/.gitkeep
================================================
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/index.html
================================================
HealthCheckLoading...
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
export function getBaseUrl() {
return document.getElementsByTagName('base')[0].href;
}
const providers = [
{ provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic(providers).bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/styles.css
================================================
/* You can add global styles to this file, and also import other style files */
/* Provide sufficient contrast against white background */
a {
color: #0366d6;
}
code {
color: #e01a76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/tsconfig.app.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/tsconfig.server.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs"
},
"angularCompilerOptions": {
"entryModule": "app/app.server.module#AppServerModule"
}
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/tsconfig.spec.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/src/tslint.json
================================================
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"module": "esnext",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
},
"angularCompilerOptions": {
"strictMetadataEmit": true
}
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/tslint.json
================================================
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"no-output-on-prefix": true,
"no-inputs-metadata-property": true,
"no-outputs-metadata-property": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-output-rename": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}
================================================
FILE: Chapter_02/HealthCheck/ClientApp/update-npm.bat
================================================
cd %~dp0
npm update
================================================
FILE: Chapter_02/HealthCheck/HealthCheck.csproj
================================================
netcoreapp3.1trueLatestfalseClientApp\$(DefaultItemExcludes);$(SpaRoot)node_modules\**falseHealthCheck%(DistFiles.Identity)PreserveNewest
================================================
FILE: Chapter_02/HealthCheck/Pages/Error.cshtml
================================================
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
Error.
An error occurred while processing your request.
@if (Model.ShowRequestId)
{
Request ID:@Model.RequestId
}
Development Mode
Swapping to the Development environment displays detailed information about the error that occurred.
The Development environment shouldn't be enabled for deployed applications.
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
and restarting the app.
================================================
FILE: Chapter_02/HealthCheck/Pages/Error.cshtml.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace HealthCheck.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
private readonly ILogger _logger;
public ErrorModel(ILogger logger)
{
_logger = logger;
}
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
================================================
FILE: Chapter_02/HealthCheck/Pages/_ViewImports.cshtml
================================================
@using HealthCheck
@namespace HealthCheck.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
================================================
FILE: Chapter_02/HealthCheck/Program.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace HealthCheck
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
}
}
================================================
FILE: Chapter_02/HealthCheck/Startup.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace HealthCheck
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles(new StaticFileOptions()
{
OnPrepareResponse = (context) =>
{
// Retrieve cache configuration from appsettings.json
context.Context.Response.Headers["Cache-Control"] =
Configuration["StaticFiles:Headers:Cache-Control"];
context.Context.Response.Headers["Pragma"] =
Configuration["StaticFiles:Headers:Pragma"];
context.Context.Response.Headers["Expires"] =
Configuration["StaticFiles:Headers:Expires"];
}
});
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
================================================
FILE: Chapter_02/HealthCheck/appsettings.Development.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"StaticFiles": {
"Headers": {
"Cache-Control": "no-cache, no-store",
"Pragma": "no-cache",
"Expires": "-1"
}
}
}
================================================
FILE: Chapter_02/HealthCheck/appsettings.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"StaticFiles": {
"Headers": {
"Cache-Control": "max-age=3600",
"Pragma": "cache",
"Expires": null
}
}
}
================================================
FILE: Chapter_02/HealthCheck/libman.json
================================================
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": []
}
================================================
FILE: Chapter_02/HealthCheck/wwwroot/test.html
================================================
Time for a test!
Hello there!
This is a test to see if the StaticFiles middleware is working properly.
What about the client-side cache? Does it work or not?
It seems like we can configure it: we disabled it during development,
and enabled it in production!
================================================
FILE: Chapter_02.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29209.62
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthCheck", "Chapter_02\HealthCheck\HealthCheck.csproj", "{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F0EE0686-783C-472C-8B14-22385361AEE1}
EndGlobalSection
EndGlobal
================================================
FILE: Chapter_03/HealthCheck/.gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
bin/
Bin/
obj/
Obj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
orleans.codegen.cs
/node_modules
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
================================================
FILE: Chapter_03/HealthCheck/ClientApp/.editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: Chapter_03/HealthCheck/ClientApp/.gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
================================================
FILE: Chapter_03/HealthCheck/ClientApp/README.md
================================================
# HealthCheck
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: Chapter_03/HealthCheck/ClientApp/angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"HealthCheck": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"progress": true,
"extractCss": true,
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": ["src/assets"],
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "HealthCheck:build"
},
"configurations": {
"production": {
"browserTarget": "HealthCheck:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "HealthCheck:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [ "src/styles.css" ],
"scripts": [],
"assets": ["src/assets"]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["src/tsconfig.app.json", "src/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist-server",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.server.json"
},
"configurations": {
"dev": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": true
},
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false
}
}
}
}
},
"HealthCheck-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "HealthCheck:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": ["**/node_modules/**"]
}
}
}
}
},
"defaultProject": "HealthCheck"
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/browserslist
================================================
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
# IE 9-11
================================================
FILE: Chapter_03/HealthCheck/ClientApp/e2e/protractor.conf.js
================================================
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require("jasmine-spec-reporter");
exports.config = {
allScriptsTimeout: 11000,
specs: ["./src/**/*.e2e-spec.ts"],
capabilities: {
browserName: "chrome"
},
directConnect: true,
baseUrl: "http://localhost:4200/",
framework: "jasmine",
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require("ts-node").register({
project: require("path").join(__dirname, "./tsconfig.e2e.json")
});
jasmine
.getEnv()
.addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
================================================
FILE: Chapter_03/HealthCheck/ClientApp/e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getMainHeading()).toEqual('Hello, world!');
});
});
================================================
FILE: Chapter_03/HealthCheck/ClientApp/e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getMainHeading() {
return element(by.css('app-root h1')).getText();
}
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/e2e/tsconfig.e2e.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/package.json
================================================
{
"name": "healthcheck",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "echo Starting... && ng serve",
"build": "ng build",
"build:ssr": "ng run HealthCheck:server:dev",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "9.0.0",
"@angular/common": "9.0.0",
"@angular/compiler": "9.0.0",
"@angular/core": "9.0.0",
"@angular/forms": "9.0.0",
"@angular/platform-browser": "9.0.0",
"@angular/platform-browser-dynamic": "9.0.0",
"@angular/platform-server": "9.0.0",
"@angular/router": "9.0.0",
"@nguniversal/module-map-ngfactory-loader": "9.0.0-next.9",
"aspnet-prerendering": "3.0.1",
"bootstrap": "4.4.1",
"core-js": "3.6.1",
"jquery": "3.5.1",
"oidc-client": "1.9.1",
"popper.js": "1.16.0",
"rxjs": "6.5.4",
"zone.js": "0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.900.0",
"@angular/cli": "9.0.0",
"@angular/compiler-cli": "9.0.0",
"@angular/language-service": "9.0.0",
"@types/jasmine": "3.5.0",
"@types/jasminewd2": "2.0.8",
"@types/node": "13.1.1",
"codelyzer": "5.2.1",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "4.4.1",
"karma-chrome-launcher": "3.1.0",
"karma-coverage-istanbul-reporter": "2.1.1",
"karma-jasmine": "2.0.1",
"karma-jasmine-html-reporter": "1.5.1",
"typescript": "3.7.5"
},
"optionalDependencies": {
"node-sass": "4.13.0",
"protractor": "5.4.2",
"ts-node": "5.0.1",
"tslint": "5.20.1"
}
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/app/app.component.html
================================================
Client-side navigation. For example, click Counter then Back to return here.
Angular CLI integration. In development mode, there's no need to run ng serve. It runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.
Efficient production builds. In production mode, development-time features are disabled, and your dotnet publish configuration automatically invokes ng build to produce minified, ahead-of-time compiled JavaScript files.
The ClientApp subdirectory is a standard Angular CLI application. If you open a command prompt in that directory, you can run any ng command (e.g., ng test), or use npm to install extra packages into it.
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/app/home/home.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
})
export class HomeComponent {
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/app/nav-menu/nav-menu.component.css
================================================
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/app/nav-menu/nav-menu.component.html
================================================
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/app/nav-menu/nav-menu.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent {
isExpanded = false;
collapse() {
this.isExpanded = false;
}
toggle() {
this.isExpanded = !this.isExpanded;
}
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/assets/.gitkeep
================================================
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/index.html
================================================
HealthCheckLoading...
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
export function getBaseUrl() {
return document.getElementsByTagName('base')[0].href;
}
const providers = [
{ provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic(providers).bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/styles.css
================================================
/* You can add global styles to this file, and also import other style files */
/* Provide sufficient contrast against white background */
a {
color: #0366d6;
}
code {
color: #e01a76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/tsconfig.app.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/tsconfig.server.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs"
},
"angularCompilerOptions": {
"entryModule": "app/app.server.module#AppServerModule"
}
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/tsconfig.spec.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/src/tslint.json
================================================
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"module": "esnext",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
},
"angularCompilerOptions": {
"strictMetadataEmit": true
}
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/tslint.json
================================================
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"no-output-on-prefix": true,
"no-inputs-metadata-property": true,
"no-outputs-metadata-property": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-output-rename": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}
================================================
FILE: Chapter_03/HealthCheck/ClientApp/update-npm.bat
================================================
cd %~dp0
npm update
================================================
FILE: Chapter_03/HealthCheck/CustomHealthCheckOptions.cs
================================================
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http;
using System.Linq;
using System.Net.Mime;
using System.Text.Json;
namespace HealthCheck
{
public class CustomHealthCheckOptions : HealthCheckOptions
{
public CustomHealthCheckOptions() : base()
{
var jsonSerializerOptions = new JsonSerializerOptions()
{
WriteIndented = true
};
ResponseWriter = async (c, r) =>
{
c.Response.ContentType = MediaTypeNames.Application.Json;
c.Response.StatusCode = StatusCodes.Status200OK;
var result = JsonSerializer.Serialize(new
{
checks = r.Entries.Select(e => new
{
name = e.Key,
responseTime = e.Value.Duration.TotalMilliseconds,
status = e.Value.Status.ToString(),
description = e.Value.Description
}),
totalStatus = r.Status,
totalResponseTime = r.TotalDuration.TotalMilliseconds,
}, jsonSerializerOptions);
await c.Response.WriteAsync(result);
};
}
}
}
================================================
FILE: Chapter_03/HealthCheck/HealthCheck.csproj
================================================
netcoreapp3.1trueLatestfalseClientApp\$(DefaultItemExcludes);$(SpaRoot)node_modules\**falseHealthCheck%(DistFiles.Identity)PreserveNewest
================================================
FILE: Chapter_03/HealthCheck/ICMPHealthCheck.cs
================================================
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
namespace HealthCheck
{
public class ICMPHealthCheck : IHealthCheck
{
private string Host { get; set; }
private int Timeout { get; set; }
public ICMPHealthCheck(string host, int timeout)
{
Host = host;
Timeout = timeout;
}
public async Task CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
using (var ping = new Ping())
{
var reply = await ping.SendPingAsync(Host);
switch (reply.Status)
{
case IPStatus.Success:
var msg = String.Format(
"IMCP to {0} took {1} ms.",
Host,
reply.RoundtripTime);
return (reply.RoundtripTime > Timeout)
? HealthCheckResult.Degraded(msg)
: HealthCheckResult.Healthy(msg);
default:
var err = String.Format(
"IMCP to {0} failed: {1}",
Host,
reply.Status);
return HealthCheckResult.Unhealthy(err);
}
}
}
catch (Exception e)
{
var err = String.Format(
"IMCP to {0} failed: {1}",
Host,
e.Message);
return HealthCheckResult.Unhealthy(err);
}
}
}
}
================================================
FILE: Chapter_03/HealthCheck/Pages/Error.cshtml
================================================
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
Error.
An error occurred while processing your request.
@if (Model.ShowRequestId)
{
Request ID:@Model.RequestId
}
Development Mode
Swapping to the Development environment displays detailed information about the error that occurred.
The Development environment shouldn't be enabled for deployed applications.
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
and restarting the app.
================================================
FILE: Chapter_03/HealthCheck/Pages/Error.cshtml.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace HealthCheck.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
private readonly ILogger _logger;
public ErrorModel(ILogger logger)
{
_logger = logger;
}
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
================================================
FILE: Chapter_03/HealthCheck/Pages/_ViewImports.cshtml
================================================
@using HealthCheck
@namespace HealthCheck.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
================================================
FILE: Chapter_03/HealthCheck/Program.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace HealthCheck
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
}
}
================================================
FILE: Chapter_03/HealthCheck/Startup.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace HealthCheck
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddHealthChecks()
.AddCheck("ICMP_01", new ICMPHealthCheck("www.ryadel.com", 100))
.AddCheck("ICMP_02", new ICMPHealthCheck("www.google.com", 100))
.AddCheck("ICMP_03", new ICMPHealthCheck("www.does-not-exist.com", 100));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles(new StaticFileOptions()
{
OnPrepareResponse = (context) =>
{
// Retrieve cache configuration from appsettings.json
context.Context.Response.Headers["Cache-Control"] =
Configuration["StaticFiles:Headers:Cache-Control"];
context.Context.Response.Headers["Pragma"] =
Configuration["StaticFiles:Headers:Pragma"];
context.Context.Response.Headers["Expires"] =
Configuration["StaticFiles:Headers:Expires"];
}
});
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseHealthChecks("/hc", new CustomHealthCheckOptions());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
================================================
FILE: Chapter_03/HealthCheck/appsettings.Development.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"StaticFiles": {
"Headers": {
"Cache-Control": "no-cache, no-store",
"Pragma": "no-cache",
"Expires": "-1"
}
}
}
================================================
FILE: Chapter_03/HealthCheck/appsettings.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"StaticFiles": {
"Headers": {
"Cache-Control": "max-age=3600",
"Pragma": "cache",
"Expires": null
}
}
}
================================================
FILE: Chapter_03/HealthCheck/libman.json
================================================
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": []
}
================================================
FILE: Chapter_03/HealthCheck/wwwroot/test.html
================================================
Time for a test!
Hello there!
This is a test to see if the StaticFiles middleware is working properly.
What about the client-side cache? Does it work or not?
It seems like we can configure it: we disabled it during development,
and enabled it in production!
================================================
FILE: Chapter_03.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29209.62
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthCheck", "Chapter_03\HealthCheck\HealthCheck.csproj", "{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DC82B04-93AA-4B83-AE82-759A0E21DFF5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F0EE0686-783C-472C-8B14-22385361AEE1}
EndGlobalSection
EndGlobal
================================================
FILE: Chapter_04/WorldCities/.gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
bin/
Bin/
obj/
Obj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
orleans.codegen.cs
/node_modules
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
================================================
FILE: Chapter_04/WorldCities/ClientApp/.editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: Chapter_04/WorldCities/ClientApp/.gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
================================================
FILE: Chapter_04/WorldCities/ClientApp/README.md
================================================
# WorldCities
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: Chapter_04/WorldCities/ClientApp/angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"WorldCities": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"progress": true,
"extractCss": true,
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": ["src/assets"],
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "WorldCities:build"
},
"configurations": {
"production": {
"browserTarget": "WorldCities:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "WorldCities:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": ["styles.css"],
"scripts": [],
"assets": ["src/assets"]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["src/tsconfig.app.json", "src/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist-server",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.server.json"
},
"configurations": {
"dev": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": true
},
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false
}
}
}
}
},
"WorldCities-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "WorldCities:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": ["**/node_modules/**"]
}
}
}
}
},
"defaultProject": "WorldCities"
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/browserslist
================================================
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
# IE 9-11
================================================
FILE: Chapter_04/WorldCities/ClientApp/e2e/protractor.conf.js
================================================
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require("jasmine-spec-reporter");
exports.config = {
allScriptsTimeout: 11000,
specs: ["./src/**/*.e2e-spec.ts"],
capabilities: {
browserName: "chrome"
},
directConnect: true,
baseUrl: "http://localhost:4200/",
framework: "jasmine",
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require("ts-node").register({
project: require("path").join(__dirname, "./tsconfig.e2e.json")
});
jasmine
.getEnv()
.addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
================================================
FILE: Chapter_04/WorldCities/ClientApp/e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getMainHeading()).toEqual('Hello, world!');
});
});
================================================
FILE: Chapter_04/WorldCities/ClientApp/e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getMainHeading() {
return element(by.css('app-root h1')).getText();
}
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/e2e/tsconfig.e2e.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/package.json
================================================
{
"name": "worldcities",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "echo Starting... && ng serve",
"build": "ng build",
"build:ssr": "ng run WorldCities:server:dev",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "9.0.0",
"@angular/common": "9.0.0",
"@angular/compiler": "9.0.0",
"@angular/core": "9.0.0",
"@angular/forms": "9.0.0",
"@angular/platform-browser": "9.0.0",
"@angular/platform-browser-dynamic": "9.0.0",
"@angular/platform-server": "9.0.0",
"@angular/router": "9.0.0",
"@nguniversal/module-map-ngfactory-loader": "9.0.0-next.9",
"aspnet-prerendering": "3.0.1",
"bootstrap": "4.4.1",
"core-js": "3.6.1",
"jquery": "3.5.1",
"oidc-client": "1.9.1",
"popper.js": "1.16.0",
"rxjs": "6.5.4",
"zone.js": "0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.900.0",
"@angular/cli": "9.0.0",
"@angular/compiler-cli": "9.0.0",
"@angular/language-service": "9.0.0",
"@types/jasmine": "3.5.0",
"@types/jasminewd2": "2.0.8",
"@types/node": "13.1.1",
"codelyzer": "5.2.1",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "4.4.1",
"karma-chrome-launcher": "3.1.0",
"karma-coverage-istanbul-reporter": "2.1.1",
"karma-jasmine": "2.0.1",
"karma-jasmine-html-reporter": "1.5.1",
"typescript": "3.7.5"
},
"optionalDependencies": {
"node-sass": "4.13.0",
"protractor": "5.4.2",
"ts-node": "5.0.1",
"tslint": "5.20.1"
}
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/app/app.component.html
================================================
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
title = 'app';
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/app/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { HomeComponent } from './home/home.component';
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
HomeComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
HttpClientModule,
FormsModule,
RouterModule.forRoot([
{ path: '', component: HomeComponent, pathMatch: 'full' }
])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/app/app.server.module.ts
================================================
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { AppComponent } from './app.component';
import { AppModule } from './app.module';
@NgModule({
imports: [AppModule, ServerModule, ModuleMapLoaderModule],
bootstrap: [AppComponent]
})
export class AppServerModule { }
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/app/home/home.component.html
================================================
Hello, world!
Welcome to your new single-page application, built with:
Client-side navigation. For example, click Counter then Back to return here.
Angular CLI integration. In development mode, there's no need to run ng serve. It runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.
Efficient production builds. In production mode, development-time features are disabled, and your dotnet publish configuration automatically invokes ng build to produce minified, ahead-of-time compiled JavaScript files.
The ClientApp subdirectory is a standard Angular CLI application. If you open a command prompt in that directory, you can run any ng command (e.g., ng test), or use npm to install extra packages into it.
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/app/home/home.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
})
export class HomeComponent {
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/app/nav-menu/nav-menu.component.css
================================================
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/app/nav-menu/nav-menu.component.html
================================================
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/app/nav-menu/nav-menu.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent {
isExpanded = false;
collapse() {
this.isExpanded = false;
}
toggle() {
this.isExpanded = !this.isExpanded;
}
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/assets/.gitkeep
================================================
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/index.html
================================================
WorldCitiesLoading...
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
export function getBaseUrl() {
return document.getElementsByTagName('base')[0].href;
}
const providers = [
{ provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic(providers).bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/styles.css
================================================
/* You can add global styles to this file, and also import other style files */
/* Provide sufficient contrast against white background */
a {
color: #0366d6;
}
code {
color: #e01a76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/tsconfig.app.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/tsconfig.server.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs"
},
"angularCompilerOptions": {
"entryModule": "app/app.server.module#AppServerModule"
}
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/tsconfig.spec.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/src/tslint.json
================================================
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"module": "esnext",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/tslint.json
================================================
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"no-output-on-prefix": true,
"no-inputs-metadata-property": true,
"no-outputs-metadata-property": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-output-rename": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}
================================================
FILE: Chapter_04/WorldCities/ClientApp/update-npm.bat
================================================
cd %~dp0
npm update
================================================
FILE: Chapter_04/WorldCities/Controllers/CitiesController.cs
================================================
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 WorldCities.Data;
using WorldCities.Data.Models;
namespace WorldCities.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CitiesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public CitiesController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/Cities
[HttpGet]
public async Task>> GetCities()
{
return await _context.Cities.ToListAsync();
}
// GET: api/Cities/5
[HttpGet("{id}")]
public async Task> GetCity(int id)
{
var city = await _context.Cities.FindAsync(id);
if (city == null)
{
return NotFound();
}
return city;
}
// PUT: api/Cities/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task PutCity(int id, City city)
{
if (id != city.Id)
{
return BadRequest();
}
_context.Entry(city).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CityExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Cities
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task> PostCity(City city)
{
_context.Cities.Add(city);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCity", new { id = city.Id }, city);
}
// DELETE: api/Cities/5
[HttpDelete("{id}")]
public async Task> DeleteCity(int id)
{
var city = await _context.Cities.FindAsync(id);
if (city == null)
{
return NotFound();
}
_context.Cities.Remove(city);
await _context.SaveChangesAsync();
return city;
}
private bool CityExists(int id)
{
return _context.Cities.Any(e => e.Id == id);
}
}
}
================================================
FILE: Chapter_04/WorldCities/Controllers/CountriesController.cs
================================================
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 WorldCities.Data;
using WorldCities.Data.Models;
namespace WorldCities.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CountriesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public CountriesController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/Countries
[HttpGet]
public async Task>> GetCountries()
{
return await _context.Countries.ToListAsync();
}
// GET: api/Countries/5
[HttpGet("{id}")]
public async Task> GetCountry(int id)
{
var country = await _context.Countries.FindAsync(id);
if (country == null)
{
return NotFound();
}
return country;
}
// PUT: api/Countries/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task PutCountry(int id, Country country)
{
if (id != country.Id)
{
return BadRequest();
}
_context.Entry(country).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CountryExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Countries
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task> PostCountry(Country country)
{
_context.Countries.Add(country);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCountry", new { id = country.Id }, country);
}
// DELETE: api/Countries/5
[HttpDelete("{id}")]
public async Task> DeleteCountry(int id)
{
var country = await _context.Countries.FindAsync(id);
if (country == null)
{
return NotFound();
}
_context.Countries.Remove(country);
await _context.SaveChangesAsync();
return country;
}
private bool CountryExists(int id)
{
return _context.Countries.Any(e => e.Id == id);
}
}
}
================================================
FILE: Chapter_04/WorldCities/Controllers/SeedController.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WorldCities.Data;
using OfficeOpenXml;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using WorldCities.Data.Models;
using System.Text.Json;
namespace WorldCities.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class SeedController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly IWebHostEnvironment _env;
public SeedController(
ApplicationDbContext context,
IWebHostEnvironment env)
{
_context = context;
_env = env;
}
[HttpGet]
public async Task Import()
{
// NOTE: This method has been updated on 2020.09.13.
// The new version is more efficient than the code described in the book's Chapter 4.
// ref.: https://github.com/PacktPublishing/ASP.NET-Core-3-and-Angular-9-Third-Edition/issues/15
var path = Path.Combine(
_env.ContentRootPath,
String.Format("Data/Source/worldcities.xlsx"));
using (var stream = new FileStream(
path,
FileMode.Open,
FileAccess.Read))
{
using (var ep = new ExcelPackage(stream))
{
// get the first worksheet
var ws = ep.Workbook.Worksheets[0];
// initialize the record counters
var nCountries = 0;
var nCities = 0;
#region Import all Countries
// create a list containing all the countries already existing
// into the Database (it will be empty on first run).
var lstCountries = _context.Countries.ToList();
// iterates through all rows, skipping the first one
for (int nRow = 2;
nRow <= ws.Dimension.End.Row;
nRow++)
{
var row = ws.Cells[nRow, 1, nRow, ws.Dimension.End.Column];
var name = row[nRow, 5].GetValue();
// does this country already exist in the database?
if (lstCountries.Where(c => c.Name == name).Count() == 0)
{
// create the Country entity and fill it with xlsx data
var country = new Country();
country.Name = name;
country.ISO2 = row[nRow, 6].GetValue();
country.ISO3 = row[nRow, 7].GetValue();
// add the new country to the DB context
_context.Countries.Add(country);
// store the country to retrieve its Id later on
lstCountries.Add(country);
// increment the counter
nCountries++;
}
}
// save all the countries into the Database
if (nCountries > 0) await _context.SaveChangesAsync();
#endregion
#region Import all Cities
// create a list containing all the cities already existing
// into the Database (it will be empty on first run).
var lstCities = _context.Cities.ToList();
// iterates through all rows, skipping the first one
for (int nRow = 2;
nRow <= ws.Dimension.End.Row;
nRow++)
{
var row = ws.Cells[nRow, 1, nRow, ws.Dimension.End.Column];
var name = row[nRow, 1].GetValue();
var name_ASCII = row[nRow, 2].GetValue();
var countryName = row[nRow, 5].GetValue();
var lat = row[nRow, 3].GetValue();
var lon = row[nRow, 4].GetValue();
// retrieve country and countryId
var country = lstCountries.Where(c => c.Name == countryName)
.FirstOrDefault();
var countryId = country.Id;
// does this city already exist in the database?
if (lstCities.Where(
c => c.Name == name
&& c.Lat == lat
&& c.Lon == lon
&& c.CountryId == countryId
).Count() == 0)
{
// create the City entity and fill it with xlsx data
var city = new City();
city.Name = name;
city.Name_ASCII = name_ASCII;
city.Lat = lat;
city.Lon = lon;
city.CountryId = countryId;
// add the new city to the DB context
_context.Cities.Add(city);
// increment the counter
nCities++;
}
}
// save all the cities into the Database
if (nCities > 0) await _context.SaveChangesAsync();
#endregion
return new JsonResult(new
{
Cities = nCities,
Countries = nCountries
});
}
}
}
}
}
================================================
FILE: Chapter_04/WorldCities/Data/ApplicationDbContext.cs
================================================
using Microsoft.EntityFrameworkCore;
using WorldCities.Data.Models;
namespace WorldCities.Data
{
public class ApplicationDbContext : DbContext
{
#region Constructor
public ApplicationDbContext() : base()
{
}
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
#endregion Constructor
#region Methods
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Map Entity names to DB Table names
modelBuilder.Entity().ToTable("Cities");
modelBuilder.Entity().ToTable("Countries");
}
#endregion Methods
#region Properties
public DbSet Cities { get; set; }
public DbSet Countries { get; set; }
#endregion Properties
}
}
================================================
FILE: Chapter_04/WorldCities/Data/Migrations/20191123030140_Initial.Designer.cs
================================================
//
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using WorldCities.Data;
namespace WorldCities.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20191123030140_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("CountryId")
.HasColumnType("int");
b.Property("Lat")
.HasColumnType("decimal(7,4)");
b.Property("Lon")
.HasColumnType("decimal(7,4)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.Property("Name_ASCII")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("Cities");
});
modelBuilder.Entity("WorldCities.Data.Models.Country", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("ISO2")
.HasColumnType("nvarchar(max)");
b.Property("ISO3")
.HasColumnType("nvarchar(max)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.HasOne("WorldCities.Data.Models.Country", "Country")
.WithMany("Cities")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
================================================
FILE: Chapter_04/WorldCities/Data/Migrations/20191123030140_Initial.cs
================================================
using Microsoft.EntityFrameworkCore.Migrations;
namespace WorldCities.Data.Migrations
{
public partial class Initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Countries",
columns: table => new
{
Id = table.Column(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column(nullable: true),
ISO2 = table.Column(nullable: true),
ISO3 = table.Column(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Countries", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Cities",
columns: table => new
{
Id = table.Column(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column(nullable: true),
Name_ASCII = table.Column(nullable: true),
Lat = table.Column(type: "decimal(7,4)", nullable: false),
Lon = table.Column(type: "decimal(7,4)", nullable: false),
CountryId = table.Column(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Cities", x => x.Id);
table.ForeignKey(
name: "FK_Cities_Countries_CountryId",
column: x => x.CountryId,
principalTable: "Countries",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Cities_CountryId",
table: "Cities",
column: "CountryId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Cities");
migrationBuilder.DropTable(
name: "Countries");
}
}
}
================================================
FILE: Chapter_04/WorldCities/Data/Migrations/ApplicationDbContextModelSnapshot.cs
================================================
//
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using WorldCities.Data;
namespace WorldCities.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("CountryId")
.HasColumnType("int");
b.Property("Lat")
.HasColumnType("decimal(7,4)");
b.Property("Lon")
.HasColumnType("decimal(7,4)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.Property("Name_ASCII")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("Cities");
});
modelBuilder.Entity("WorldCities.Data.Models.Country", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("ISO2")
.HasColumnType("nvarchar(max)");
b.Property("ISO3")
.HasColumnType("nvarchar(max)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.HasOne("WorldCities.Data.Models.Country", "Country")
.WithMany("Cities")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
================================================
FILE: Chapter_04/WorldCities/Data/Models/City.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace WorldCities.Data.Models
{
public class City
{
#region Constructor
public City()
{
}
#endregion
#region Properties
///
/// The unique id and primary key for this City
///
[Key]
[Required]
public int Id { get; set; }
///
/// City name (in UTF8 format)
///
public string Name { get; set; }
///
/// City name (in ASCII format)
///
public string Name_ASCII { get; set; }
///
/// City latitude
///
[Column(TypeName = "decimal(7,4)")]
public decimal Lat { get; set; }
///
/// City longitude
///
[Column(TypeName = "decimal(7,4)")]
public decimal Lon { get; set; }
///
/// Country Id (foreign key)
///
[ForeignKey("Country")]
public int CountryId { get; set; }
#endregion
#region Navigation Properties
///
/// The country related to this city.
///
public virtual Country Country { get; set; }
#endregion
}
}
================================================
FILE: Chapter_04/WorldCities/Data/Models/Country.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace WorldCities.Data.Models
{
public class Country
{
#region Constructor
public Country()
{
}
#endregion
#region Properties
///
/// The unique id and primary key for this Country
///
[Key]
[Required]
public int Id { get; set; }
///
/// Country name (in UTF8 format)
///
public string Name { get; set; }
///
/// Country code (in ISO 3166-1 ALPHA-2 format)
///
public string ISO2 { get; set; }
///
/// Country code (in ISO 3166-1 ALPHA-3 format)
///
public string ISO3 { get; set; }
#endregion
#region Navigation Properties
///
/// A list containing all the cities related to this country.
///
public virtual List Cities { get; set; }
#endregion
}
}
================================================
FILE: Chapter_04/WorldCities/Pages/Error.cshtml
================================================
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
Error.
An error occurred while processing your request.
@if (Model.ShowRequestId)
{
Request ID:@Model.RequestId
}
Development Mode
Swapping to the Development environment displays detailed information about the error that occurred.
The Development environment shouldn't be enabled for deployed applications.
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
and restarting the app.
================================================
FILE: Chapter_04/WorldCities/Pages/Error.cshtml.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace WorldCities.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
private readonly ILogger _logger;
public ErrorModel(ILogger logger)
{
_logger = logger;
}
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
================================================
FILE: Chapter_04/WorldCities/Pages/_ViewImports.cshtml
================================================
@using WorldCities
@namespace WorldCities.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
================================================
FILE: Chapter_04/WorldCities/Program.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace WorldCities
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
}
}
================================================
FILE: Chapter_04/WorldCities/Startup.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using WorldCities.Data;
namespace WorldCities
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
// Add EntityFramework support for SqlServer.
services.AddEntityFrameworkSqlServer();
// Add ApplicationDbContext.
services.AddDbContext(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")
)
);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
================================================
FILE: Chapter_04/WorldCities/WorldCities.csproj
================================================
netcoreapp3.1trueLatestfalseClientApp\$(DefaultItemExcludes);$(SpaRoot)node_modules\**falseallruntime; build; native; contentfiles; analyzers; buildtransitive%(DistFiles.Identity)PreserveNewesttrue
================================================
FILE: Chapter_04/WorldCities/appsettings.Development.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
================================================
FILE: Chapter_04/WorldCities/appsettings.json
================================================
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=WorldCities;User Id=WorldCities;Password=MyVeryOwn$721;Integrated Security=False;MultipleActiveResultSets=True"
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
================================================
FILE: Chapter_04.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29209.62
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorldCities", "Chapter_04\WorldCities\WorldCities.csproj", "{485D17FC-2E33-462C-879A-D67B9DAF7388}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{485D17FC-2E33-462C-879A-D67B9DAF7388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{485D17FC-2E33-462C-879A-D67B9DAF7388}.Debug|Any CPU.Build.0 = Debug|Any CPU
{485D17FC-2E33-462C-879A-D67B9DAF7388}.Release|Any CPU.ActiveCfg = Release|Any CPU
{485D17FC-2E33-462C-879A-D67B9DAF7388}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F0EE0686-783C-472C-8B14-22385361AEE1}
EndGlobalSection
EndGlobal
================================================
FILE: Chapter_05/WorldCities/.gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
bin/
Bin/
obj/
Obj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
orleans.codegen.cs
/node_modules
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
================================================
FILE: Chapter_05/WorldCities/ClientApp/.editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: Chapter_05/WorldCities/ClientApp/.gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
================================================
FILE: Chapter_05/WorldCities/ClientApp/README.md
================================================
# WorldCities
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: Chapter_05/WorldCities/ClientApp/angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"WorldCities": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"progress": true,
"extractCss": true,
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "WorldCities:build"
},
"configurations": {
"production": {
"browserTarget": "WorldCities:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "WorldCities:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"styles.css"
],
"scripts": [],
"assets": [
"src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist-server",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.server.json"
},
"configurations": {
"dev": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": true
},
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false
}
}
}
}
},
"WorldCities-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "WorldCities:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "WorldCities"
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/browserslist
================================================
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
# IE 9-11
================================================
FILE: Chapter_05/WorldCities/ClientApp/e2e/protractor.conf.js
================================================
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require("jasmine-spec-reporter");
exports.config = {
allScriptsTimeout: 11000,
specs: ["./src/**/*.e2e-spec.ts"],
capabilities: {
browserName: "chrome"
},
directConnect: true,
baseUrl: "http://localhost:4200/",
framework: "jasmine",
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require("ts-node").register({
project: require("path").join(__dirname, "./tsconfig.e2e.json")
});
jasmine
.getEnv()
.addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
================================================
FILE: Chapter_05/WorldCities/ClientApp/e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getMainHeading()).toEqual('Hello, world!');
});
});
================================================
FILE: Chapter_05/WorldCities/ClientApp/e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getMainHeading() {
return element(by.css('app-root h1')).getText();
}
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/e2e/tsconfig.e2e.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/package.json
================================================
{
"name": "worldcities",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "echo Starting... && ng serve",
"build": "ng build",
"build:ssr": "ng run WorldCities:server:dev",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "9.0.0",
"@angular/cdk": "9.0.0",
"@angular/common": "9.0.0",
"@angular/compiler": "9.0.0",
"@angular/core": "9.0.0",
"@angular/forms": "9.0.0",
"@angular/material": "9.0.0",
"@angular/platform-browser": "9.0.0",
"@angular/platform-browser-dynamic": "9.0.0",
"@angular/platform-server": "9.0.0",
"@angular/router": "9.0.0",
"@nguniversal/module-map-ngfactory-loader": "9.0.0-next.9",
"aspnet-prerendering": "3.0.1",
"bootstrap": "4.4.1",
"core-js": "3.6.1",
"hammerjs": "2.0.8",
"jquery": "3.5.1",
"oidc-client": "1.9.1",
"popper.js": "1.16.0",
"rxjs": "6.5.4",
"zone.js": "0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.900.0",
"@angular/cli": "9.0.0",
"@angular/compiler-cli": "9.0.0",
"@angular/language-service": "9.0.0",
"@types/jasmine": "3.5.0",
"@types/jasminewd2": "2.0.8",
"@types/node": "13.1.1",
"codelyzer": "5.2.1",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "4.4.1",
"karma-chrome-launcher": "3.1.0",
"karma-coverage-istanbul-reporter": "2.1.1",
"karma-jasmine": "2.0.1",
"karma-jasmine-html-reporter": "1.5.1",
"typescript": "3.7.5"
},
"optionalDependencies": {
"node-sass": "4.13.0",
"protractor": "5.4.2",
"ts-node": "5.0.1",
"tslint": "5.20.1"
}
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/angular-material.module.ts
================================================
import { NgModule } from '@angular/core';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatInputModule } from '@angular/material/input';
@NgModule({
imports: [
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatInputModule
],
exports: [
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatInputModule
]
})
export class AngularMaterialModule { }
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/app.component.html
================================================
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
title = 'app';
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { HomeComponent } from './home/home.component';
import { CitiesComponent } from './cities/cities.component';
import { CountriesComponent } from './countries/countries.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AngularMaterialModule } from './angular-material.module';
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
HomeComponent,
CitiesComponent,
CountriesComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
HttpClientModule,
FormsModule,
RouterModule.forRoot([
{ path: '', component: HomeComponent, pathMatch: 'full' },
{ path: 'cities', component: CitiesComponent },
{ path: 'countries', component: CountriesComponent }
]),
BrowserAnimationsModule,
AngularMaterialModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/app.server.module.ts
================================================
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { AppComponent } from './app.component';
import { AppModule } from './app.module';
@NgModule({
imports: [AppModule, ServerModule, ModuleMapLoaderModule],
bootstrap: [AppComponent]
})
export class AppServerModule { }
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/cities/_clientSidePagination/_README.txt
================================================
The files contained in this folder have been added in 2020.09.13 to fix the "client-side pagination" technique of the MatPaginator component in book's Chapter 5
(pages 224-226), before it gets replaced with the server-side pagination technique within that same chapter later on.
In order to make them work, do the following:
- replace the cities.component.html and cities.components.ts files in the /ClientApp/sec/app/Cities/ folder with the corresponding files contained in this folder.
- patch the GetCities() method in the /Controllers/CitiesController.cs file in the following way:
// GET: api/Cities
[HttpGet]
public async Task>> GetCities()
{
return await _context.Cities.ToListAsync();
}
IMPORTANT: the "client-side pagination" technique is way less efficient than its "server-side" counterpart and has only been added there for demostration purposes:
see GitHub issue #16 ( https://github.com/PacktPublishing/ASP.NET-Core-3-and-Angular-9-Third-Edition/issues/16 ) for further details.
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/cities/_clientSidePagination/cities.component.html
================================================
Cities
Here's a list of cities: feel free to play with it.
Client-side navigation. For example, click Counter then Back to return here.
Angular CLI integration. In development mode, there's no need to run ng serve. It runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.
Efficient production builds. In production mode, development-time features are disabled, and your dotnet publish configuration automatically invokes ng build to produce minified, ahead-of-time compiled JavaScript files.
The ClientApp subdirectory is a standard Angular CLI application. If you open a command prompt in that directory, you can run any ng command (e.g., ng test), or use npm to install extra packages into it.
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/home/home.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
})
export class HomeComponent {
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/nav-menu/nav-menu.component.css
================================================
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/nav-menu/nav-menu.component.html
================================================
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/app/nav-menu/nav-menu.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent {
isExpanded = false;
collapse() {
this.isExpanded = false;
}
toggle() {
this.isExpanded = !this.isExpanded;
}
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/assets/.gitkeep
================================================
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/index.html
================================================
WorldCitiesLoading...
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/main.ts
================================================
import 'hammerjs';
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
export function getBaseUrl() {
return document.getElementsByTagName('base')[0].href;
}
const providers = [
{ provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic(providers).bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/styles.css
================================================
/* You can add global styles to this file, and also import other style files */
/* Provide sufficient contrast against white background */
a {
color: #0366d6;
}
code {
color: #e01a76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/tsconfig.app.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/tsconfig.server.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs"
},
"angularCompilerOptions": {
"entryModule": "app/app.server.module#AppServerModule"
}
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/tsconfig.spec.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/src/tslint.json
================================================
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"module": "esnext",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/tslint.json
================================================
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"no-output-on-prefix": true,
"no-inputs-metadata-property": true,
"no-outputs-metadata-property": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-output-rename": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}
================================================
FILE: Chapter_05/WorldCities/ClientApp/update-npm.bat
================================================
cd %~dp0
npm update
================================================
FILE: Chapter_05/WorldCities/Controllers/CitiesController.cs
================================================
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 WorldCities.Data;
using WorldCities.Data.Models;
namespace WorldCities.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CitiesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public CitiesController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/Cities
// GET: api/Cities/?pageIndex=0&pageSize=10
// GET: api/Cities/?pageIndex=0&pageSize=10&sortColumn=name&sortOrder=asc
// GET: api/Cities/?pageIndex=0&pageSize=10&sortColumn=name&sortOrder=asc&filterColumn=name&filterQuery=york
[HttpGet]
public async Task>> GetCities(
int pageIndex = 0,
int pageSize = 10,
string sortColumn = null,
string sortOrder = null,
string filterColumn = null,
string filterQuery = null)
{
return await ApiResult.CreateAsync(
_context.Cities,
pageIndex,
pageSize,
sortColumn,
sortOrder,
filterColumn,
filterQuery);
}
// GET: api/Cities/5
[HttpGet("{id}")]
public async Task> GetCity(int id)
{
var city = await _context.Cities.FindAsync(id);
if (city == null)
{
return NotFound();
}
return city;
}
// PUT: api/Cities/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task PutCity(int id, City city)
{
if (id != city.Id)
{
return BadRequest();
}
_context.Entry(city).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CityExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Cities
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task> PostCity(City city)
{
_context.Cities.Add(city);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCity", new { id = city.Id }, city);
}
// DELETE: api/Cities/5
[HttpDelete("{id}")]
public async Task> DeleteCity(int id)
{
var city = await _context.Cities.FindAsync(id);
if (city == null)
{
return NotFound();
}
_context.Cities.Remove(city);
await _context.SaveChangesAsync();
return city;
}
private bool CityExists(int id)
{
return _context.Cities.Any(e => e.Id == id);
}
}
}
================================================
FILE: Chapter_05/WorldCities/Controllers/CountriesController.cs
================================================
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 WorldCities.Data;
using WorldCities.Data.Models;
namespace WorldCities.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CountriesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public CountriesController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/Cities
// GET: api/Countries/?pageIndex=0&pageSize=10
// GET: api/Countries/?pageIndex=0&pageSize=10&sortColumn=name&sortOrder=asc
// GET: api/Countries/?pageIndex=0&pageSize=10&sortColumn=name&sortOrder=asc&filterColumn=name&filterQuery=york
[HttpGet]
public async Task>> GetCountries(
int pageIndex = 0,
int pageSize = 10,
string sortColumn = null,
string sortOrder = null,
string filterColumn = null,
string filterQuery = null)
{
return await ApiResult.CreateAsync(
_context.Countries,
pageIndex,
pageSize,
sortColumn,
sortOrder,
filterColumn,
filterQuery);
}
// GET: api/Countries/5
[HttpGet("{id}")]
public async Task> GetCountry(int id)
{
var country = await _context.Countries.FindAsync(id);
if (country == null)
{
return NotFound();
}
return country;
}
// PUT: api/Countries/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task PutCountry(int id, Country country)
{
if (id != country.Id)
{
return BadRequest();
}
_context.Entry(country).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CountryExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Countries
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task> PostCountry(Country country)
{
_context.Countries.Add(country);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCountry", new { id = country.Id }, country);
}
// DELETE: api/Countries/5
[HttpDelete("{id}")]
public async Task> DeleteCountry(int id)
{
var country = await _context.Countries.FindAsync(id);
if (country == null)
{
return NotFound();
}
_context.Countries.Remove(country);
await _context.SaveChangesAsync();
return country;
}
private bool CountryExists(int id)
{
return _context.Countries.Any(e => e.Id == id);
}
}
}
================================================
FILE: Chapter_05/WorldCities/Controllers/SeedController.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WorldCities.Data;
using OfficeOpenXml;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using WorldCities.Data.Models;
using System.Text.Json;
namespace WorldCities.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class SeedController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly IWebHostEnvironment _env;
public SeedController(
ApplicationDbContext context,
IWebHostEnvironment env)
{
_context = context;
_env = env;
}
[HttpGet]
public async Task Import()
{
// NOTE: This method has been updated on 2020.09.13.
// The new version is more efficient than the code described in the book's Chapter 4.
// ref.: https://github.com/PacktPublishing/ASP.NET-Core-3-and-Angular-9-Third-Edition/issues/15
var path = Path.Combine(
_env.ContentRootPath,
String.Format("Data/Source/worldcities.xlsx"));
using (var stream = new FileStream(
path,
FileMode.Open,
FileAccess.Read))
{
using (var ep = new ExcelPackage(stream))
{
// get the first worksheet
var ws = ep.Workbook.Worksheets[0];
// initialize the record counters
var nCountries = 0;
var nCities = 0;
#region Import all Countries
// create a list containing all the countries already existing
// into the Database (it will be empty on first run).
var lstCountries = _context.Countries.ToList();
// iterates through all rows, skipping the first one
for (int nRow = 2;
nRow <= ws.Dimension.End.Row;
nRow++)
{
var row = ws.Cells[nRow, 1, nRow, ws.Dimension.End.Column];
var name = row[nRow, 5].GetValue();
// does this country already exist in the database?
if (lstCountries.Where(c => c.Name == name).Count() == 0)
{
// create the Country entity and fill it with xlsx data
var country = new Country();
country.Name = name;
country.ISO2 = row[nRow, 6].GetValue();
country.ISO3 = row[nRow, 7].GetValue();
// add the new country to the DB context
_context.Countries.Add(country);
// store the country to retrieve its Id later on
lstCountries.Add(country);
// increment the counter
nCountries++;
}
}
// save all the countries into the Database
if (nCountries > 0) await _context.SaveChangesAsync();
#endregion
#region Import all Cities
// create a list containing all the cities already existing
// into the Database (it will be empty on first run).
var lstCities = _context.Cities.ToList();
// iterates through all rows, skipping the first one
for (int nRow = 2;
nRow <= ws.Dimension.End.Row;
nRow++)
{
var row = ws.Cells[nRow, 1, nRow, ws.Dimension.End.Column];
var name = row[nRow, 1].GetValue();
var name_ASCII = row[nRow, 2].GetValue();
var countryName = row[nRow, 5].GetValue();
var lat = row[nRow, 3].GetValue();
var lon = row[nRow, 4].GetValue();
// retrieve country and countryId
var country = lstCountries.Where(c => c.Name == countryName)
.FirstOrDefault();
var countryId = country.Id;
// does this city already exist in the database?
if (lstCities.Where(
c => c.Name == name
&& c.Lat == lat
&& c.Lon == lon
&& c.CountryId == countryId
).Count() == 0)
{
// create the City entity and fill it with xlsx data
var city = new City();
city.Name = name;
city.Name_ASCII = name_ASCII;
city.Lat = lat;
city.Lon = lon;
city.CountryId = countryId;
// add the new city to the DB context
_context.Cities.Add(city);
// increment the counter
nCities++;
}
}
// save all the cities into the Database
if (nCities > 0) await _context.SaveChangesAsync();
#endregion
return new JsonResult(new
{
Cities = nCities,
Countries = nCountries
});
}
}
}
}
}
================================================
FILE: Chapter_05/WorldCities/Data/ApiResult.cs
================================================
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Linq.Dynamic.Core;
using System.Reflection;
namespace WorldCities.Data
{
public class ApiResult
{
///
/// Private constructor called by the CreateAsync method.
///
private ApiResult(
List data,
int count,
int pageIndex,
int pageSize,
string sortColumn,
string sortOrder,
string filterColumn,
string filterQuery)
{
Data = data;
PageIndex = pageIndex;
PageSize = pageSize;
TotalCount = count;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
SortColumn = sortColumn;
SortOrder = sortOrder;
FilterColumn = filterColumn;
FilterQuery = filterQuery;
}
#region Methods
///
/// Pages, sorts and/or filters a IQueryable source.
///
/// An IQueryable source of generic type
/// Zero-based current page index (0 = first page)
/// The actual size of each page
/// The sorting colum name
/// The sorting order ("ASC" or "DESC")
/// The filtering column name
/// The filtering query (value to lookup)
///
/// A object containing the IQueryable paged/sorted/filtered result
/// and all the relevant paging/sorting/filtering navigation info.
///
public static async Task> CreateAsync(
IQueryable source,
int pageIndex,
int pageSize,
string sortColumn = null,
string sortOrder = null,
string filterColumn = null,
string filterQuery = null)
{
if (!String.IsNullOrEmpty(filterColumn)
&& !String.IsNullOrEmpty(filterQuery)
&& IsValidProperty(filterColumn))
{
source = source.Where(
String.Format("{0}.Contains(@0)",
filterColumn),
filterQuery);
}
var count = await source.CountAsync();
if (!String.IsNullOrEmpty(sortColumn)
&& IsValidProperty(sortColumn))
{
sortOrder = !String.IsNullOrEmpty(sortOrder)
&& sortOrder.ToUpper() == "ASC"
? "ASC"
: "DESC";
source = source.OrderBy(
String.Format(
"{0} {1}",
sortColumn,
sortOrder)
);
}
source = source
.Skip(pageIndex * pageSize)
.Take(pageSize);
var data = await source.ToListAsync();
return new ApiResult(
data,
count,
pageIndex,
pageSize,
sortColumn,
sortOrder,
filterColumn,
filterQuery);
}
///
/// Checks if the given property name exists
/// to protect against SQL injection attacks
///
public static bool IsValidProperty(
string propertyName,
bool throwExceptionIfNotFound = true)
{
var prop = typeof(T).GetProperty(
propertyName,
BindingFlags.IgnoreCase |
BindingFlags.Public |
BindingFlags.Static |
BindingFlags.Instance);
if (prop == null && throwExceptionIfNotFound)
throw new NotSupportedException(
String.Format(
"ERROR: Property '{0}' does not exist.",
propertyName)
);
return prop != null;
}
#endregion
#region Properties
///
/// IQueryable data result to return.
///
public List Data { get; private set; }
///
/// Zero-based index of current page.
///
public int PageIndex { get; private set; }
///
/// Number of items contained in each page.
///
public int PageSize { get; private set; }
///
/// Total items count
///
public int TotalCount { get; private set; }
///
/// Total pages count
///
public int TotalPages { get; private set; }
///
/// TRUE if the current page has a previous page, FALSE otherwise.
///
public bool HasPreviousPage
{
get
{
return (PageIndex > 0);
}
}
///
/// TRUE if the current page has a next page, FALSE otherwise.
///
public bool HasNextPage
{
get
{
return ((PageIndex +1) < TotalPages);
}
}
///
/// Sorting Column name (or null if none set)
///
public string SortColumn { get; set; }
///
/// Sorting Order ("ASC", "DESC" or null if none set)
///
public string SortOrder { get; set; }
///
/// Filter Column name (or null if none set)
///
public string FilterColumn { get; set; }
///
/// Filter Query string
/// (to be used within the given FilterColumn)
///
public string FilterQuery { get; set; }
#endregion
}
}
================================================
FILE: Chapter_05/WorldCities/Data/ApplicationDbContext.cs
================================================
using Microsoft.EntityFrameworkCore;
using WorldCities.Data.Models;
namespace WorldCities.Data
{
public class ApplicationDbContext : DbContext
{
#region Constructor
public ApplicationDbContext() : base()
{
}
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
#endregion Constructor
#region Methods
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Map Entity names to DB Table names
modelBuilder.Entity().ToTable("Cities");
modelBuilder.Entity().ToTable("Countries");
}
#endregion Methods
#region Properties
public DbSet Cities { get; set; }
public DbSet Countries { get; set; }
#endregion Properties
}
}
================================================
FILE: Chapter_05/WorldCities/Data/Migrations/20191123030140_Initial.Designer.cs
================================================
//
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using WorldCities.Data;
namespace WorldCities.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20191123030140_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("CountryId")
.HasColumnType("int");
b.Property("Lat")
.HasColumnType("decimal(7,4)");
b.Property("Lon")
.HasColumnType("decimal(7,4)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.Property("Name_ASCII")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("Cities");
});
modelBuilder.Entity("WorldCities.Data.Models.Country", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("ISO2")
.HasColumnType("nvarchar(max)");
b.Property("ISO3")
.HasColumnType("nvarchar(max)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.HasOne("WorldCities.Data.Models.Country", "Country")
.WithMany("Cities")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
================================================
FILE: Chapter_05/WorldCities/Data/Migrations/20191123030140_Initial.cs
================================================
using Microsoft.EntityFrameworkCore.Migrations;
namespace WorldCities.Data.Migrations
{
public partial class Initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Countries",
columns: table => new
{
Id = table.Column(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column(nullable: true),
ISO2 = table.Column(nullable: true),
ISO3 = table.Column(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Countries", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Cities",
columns: table => new
{
Id = table.Column(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column(nullable: true),
Name_ASCII = table.Column(nullable: true),
Lat = table.Column(type: "decimal(7,4)", nullable: false),
Lon = table.Column(type: "decimal(7,4)", nullable: false),
CountryId = table.Column(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Cities", x => x.Id);
table.ForeignKey(
name: "FK_Cities_Countries_CountryId",
column: x => x.CountryId,
principalTable: "Countries",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Cities_CountryId",
table: "Cities",
column: "CountryId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Cities");
migrationBuilder.DropTable(
name: "Countries");
}
}
}
================================================
FILE: Chapter_05/WorldCities/Data/Migrations/ApplicationDbContextModelSnapshot.cs
================================================
//
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using WorldCities.Data;
namespace WorldCities.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("CountryId")
.HasColumnType("int");
b.Property("Lat")
.HasColumnType("decimal(7,4)");
b.Property("Lon")
.HasColumnType("decimal(7,4)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.Property("Name_ASCII")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("Cities");
});
modelBuilder.Entity("WorldCities.Data.Models.Country", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("ISO2")
.HasColumnType("nvarchar(max)");
b.Property("ISO3")
.HasColumnType("nvarchar(max)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.HasOne("WorldCities.Data.Models.Country", "Country")
.WithMany("Cities")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
================================================
FILE: Chapter_05/WorldCities/Data/Models/City.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace WorldCities.Data.Models
{
public class City
{
#region Constructor
public City()
{
}
#endregion
#region Properties
///
/// The unique id and primary key for this City
///
[Key]
[Required]
public int Id { get; set; }
///
/// City name (in UTF8 format)
///
public string Name { get; set; }
///
/// City name (in ASCII format)
///
public string Name_ASCII { get; set; }
///
/// City latitude
///
[Column(TypeName = "decimal(7,4)")]
public decimal Lat { get; set; }
///
/// City longitude
///
[Column(TypeName = "decimal(7,4)")]
public decimal Lon { get; set; }
///
/// Country Id (foreign key)
///
[ForeignKey("Country")]
public int CountryId { get; set; }
#endregion
#region Navigation Properties
///
/// The country related to this city.
///
public virtual Country Country { get; set; }
#endregion
}
}
================================================
FILE: Chapter_05/WorldCities/Data/Models/Country.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace WorldCities.Data.Models
{
public class Country
{
#region Constructor
public Country()
{
}
#endregion
#region Properties
///
/// The unique id and primary key for this Country
///
[Key]
[Required]
public int Id { get; set; }
///
/// Country name (in UTF8 format)
///
public string Name { get; set; }
///
/// Country code (in ISO 3166-1 ALPHA-2 format)
///
[JsonPropertyName("iso2")]
public string ISO2 { get; set; }
///
/// Country code (in ISO 3166-1 ALPHA-3 format)
///
[JsonPropertyName("iso3")]
public string ISO3 { get; set; }
#endregion
#region Navigation Properties
///
/// A list containing all the cities related to this country.
///
public virtual List Cities { get; set; }
#endregion
}
}
================================================
FILE: Chapter_05/WorldCities/Pages/Error.cshtml
================================================
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
Error.
An error occurred while processing your request.
@if (Model.ShowRequestId)
{
Request ID:@Model.RequestId
}
Development Mode
Swapping to the Development environment displays detailed information about the error that occurred.
The Development environment shouldn't be enabled for deployed applications.
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
and restarting the app.
================================================
FILE: Chapter_05/WorldCities/Pages/Error.cshtml.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace WorldCities.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
private readonly ILogger _logger;
public ErrorModel(ILogger logger)
{
_logger = logger;
}
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
================================================
FILE: Chapter_05/WorldCities/Pages/_ViewImports.cshtml
================================================
@using WorldCities
@namespace WorldCities.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
================================================
FILE: Chapter_05/WorldCities/Program.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace WorldCities
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
}
}
================================================
FILE: Chapter_05/WorldCities/Startup.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Text.Json;
using WorldCities.Data;
namespace WorldCities
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
.AddJsonOptions(options => {
// set this option to TRUE to indent the JSON output
options.JsonSerializerOptions.WriteIndented = true;
// set this option to NULL to use PascalCase instead of CamelCase (default)
// options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
// Add EntityFramework support for SqlServer.
services.AddEntityFrameworkSqlServer();
// Add ApplicationDbContext.
services.AddDbContext(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")
)
);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
================================================
FILE: Chapter_05/WorldCities/WorldCities.csproj
================================================
netcoreapp3.1trueLatestfalseClientApp\$(DefaultItemExcludes);$(SpaRoot)node_modules\**falseallruntime; build; native; contentfiles; analyzers; buildtransitive%(DistFiles.Identity)PreserveNewesttrue
================================================
FILE: Chapter_05/WorldCities/appsettings.Development.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
================================================
FILE: Chapter_05/WorldCities/appsettings.json
================================================
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=WorldCities;User Id=WorldCities;Password=MyVeryOwn$721;Integrated Security=False;MultipleActiveResultSets=True"
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
================================================
FILE: Chapter_05.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29209.62
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorldCities", "Chapter_05\WorldCities\WorldCities.csproj", "{004D512C-9DD7-47C3-8AEF-59D813BD8FAA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{004D512C-9DD7-47C3-8AEF-59D813BD8FAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{004D512C-9DD7-47C3-8AEF-59D813BD8FAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{004D512C-9DD7-47C3-8AEF-59D813BD8FAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{004D512C-9DD7-47C3-8AEF-59D813BD8FAA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F0EE0686-783C-472C-8B14-22385361AEE1}
EndGlobalSection
EndGlobal
================================================
FILE: Chapter_06/WorldCities/.gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
bin/
Bin/
obj/
Obj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
orleans.codegen.cs
/node_modules
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
================================================
FILE: Chapter_06/WorldCities/ClientApp/.editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: Chapter_06/WorldCities/ClientApp/.gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
================================================
FILE: Chapter_06/WorldCities/ClientApp/README.md
================================================
# WorldCities
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: Chapter_06/WorldCities/ClientApp/angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"WorldCities": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"progress": true,
"extractCss": true,
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "WorldCities:build"
},
"configurations": {
"production": {
"browserTarget": "WorldCities:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "WorldCities:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"styles.css"
],
"scripts": [],
"assets": [
"src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist-server",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.server.json"
},
"configurations": {
"dev": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": true
},
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false
}
}
}
}
},
"WorldCities-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "WorldCities:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "WorldCities"
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/browserslist
================================================
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
# IE 9-11
================================================
FILE: Chapter_06/WorldCities/ClientApp/e2e/protractor.conf.js
================================================
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require("jasmine-spec-reporter");
exports.config = {
allScriptsTimeout: 11000,
specs: ["./src/**/*.e2e-spec.ts"],
capabilities: {
browserName: "chrome"
},
directConnect: true,
baseUrl: "http://localhost:4200/",
framework: "jasmine",
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require("ts-node").register({
project: require("path").join(__dirname, "./tsconfig.e2e.json")
});
jasmine
.getEnv()
.addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
================================================
FILE: Chapter_06/WorldCities/ClientApp/e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getMainHeading()).toEqual('Hello, world!');
});
});
================================================
FILE: Chapter_06/WorldCities/ClientApp/e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getMainHeading() {
return element(by.css('app-root h1')).getText();
}
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/e2e/tsconfig.e2e.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/package.json
================================================
{
"name": "worldcities",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "echo Starting... && ng serve",
"build": "ng build",
"build:ssr": "ng run WorldCities:server:dev",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "9.0.0",
"@angular/cdk": "9.0.0",
"@angular/common": "9.0.0",
"@angular/compiler": "9.0.0",
"@angular/core": "9.0.0",
"@angular/forms": "9.0.0",
"@angular/material": "9.0.0",
"@angular/platform-browser": "9.0.0",
"@angular/platform-browser-dynamic": "9.0.0",
"@angular/platform-server": "9.0.0",
"@angular/router": "9.0.0",
"@nguniversal/module-map-ngfactory-loader": "9.0.0-next.9",
"aspnet-prerendering": "3.0.1",
"bootstrap": "4.4.1",
"core-js": "3.6.1",
"hammerjs": "2.0.8",
"jquery": "3.5.1",
"oidc-client": "1.9.1",
"popper.js": "1.16.0",
"rxjs": "6.5.4",
"zone.js": "0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.900.0",
"@angular/cli": "9.0.0",
"@angular/compiler-cli": "9.0.0",
"@angular/language-service": "9.0.0",
"@types/jasmine": "3.5.0",
"@types/jasminewd2": "2.0.8",
"@types/node": "13.1.1",
"codelyzer": "5.2.1",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "4.4.1",
"karma-chrome-launcher": "3.1.0",
"karma-coverage-istanbul-reporter": "2.1.1",
"karma-jasmine": "2.0.1",
"karma-jasmine-html-reporter": "1.5.1",
"typescript": "3.7.5"
},
"optionalDependencies": {
"node-sass": "4.13.0",
"protractor": "5.4.2",
"ts-node": "5.0.1",
"tslint": "5.20.1"
}
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/app/angular-material.module.ts
================================================
import { NgModule } from '@angular/core';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
@NgModule({
imports: [
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatInputModule,
MatSelectModule
],
exports: [
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatInputModule,
MatSelectModule
]
})
export class AngularMaterialModule { }
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/app/app.component.html
================================================
Client-side navigation. For example, click Counter then Back to return here.
Angular CLI integration. In development mode, there's no need to run ng serve. It runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.
Efficient production builds. In production mode, development-time features are disabled, and your dotnet publish configuration automatically invokes ng build to produce minified, ahead-of-time compiled JavaScript files.
The ClientApp subdirectory is a standard Angular CLI application. If you open a command prompt in that directory, you can run any ng command (e.g., ng test), or use npm to install extra packages into it.
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/app/home/home.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
})
export class HomeComponent {
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/app/nav-menu/nav-menu.component.css
================================================
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/app/nav-menu/nav-menu.component.html
================================================
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/app/nav-menu/nav-menu.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent {
isExpanded = false;
collapse() {
this.isExpanded = false;
}
toggle() {
this.isExpanded = !this.isExpanded;
}
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/assets/.gitkeep
================================================
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/index.html
================================================
WorldCitiesLoading...
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/main.ts
================================================
import 'hammerjs';
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
export function getBaseUrl() {
return document.getElementsByTagName('base')[0].href;
}
const providers = [
{ provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic(providers).bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/styles.css
================================================
/* You can add global styles to this file, and also import other style files */
/* Provide sufficient contrast against white background */
a {
color: #0366d6;
}
code {
color: #e01a76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/tsconfig.app.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/tsconfig.server.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs"
},
"angularCompilerOptions": {
"entryModule": "app/app.server.module#AppServerModule"
}
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/tsconfig.spec.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/src/tslint.json
================================================
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"module": "esnext",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/tslint.json
================================================
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"no-output-on-prefix": true,
"no-inputs-metadata-property": true,
"no-outputs-metadata-property": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-output-rename": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}
================================================
FILE: Chapter_06/WorldCities/ClientApp/update-npm.bat
================================================
cd %~dp0
npm update
================================================
FILE: Chapter_06/WorldCities/Controllers/CitiesController.cs
================================================
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 WorldCities.Data;
using WorldCities.Data.Models;
namespace WorldCities.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CitiesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public CitiesController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/Cities
// GET: api/Cities/?pageIndex=0&pageSize=10
// GET: api/Cities/?pageIndex=0&pageSize=10&sortColumn=name&sortOrder=asc
// GET: api/Cities/?pageIndex=0&pageSize=10&sortColumn=name&sortOrder=asc&filterColumn=name&filterQuery=york
[HttpGet]
public async Task>> GetCities(
int pageIndex = 0,
int pageSize = 10,
string sortColumn = null,
string sortOrder = null,
string filterColumn = null,
string filterQuery = null)
{
return await ApiResult.CreateAsync(
_context.Cities,
pageIndex,
pageSize,
sortColumn,
sortOrder,
filterColumn,
filterQuery);
}
// GET: api/Cities/5
[HttpGet("{id}")]
public async Task> GetCity(int id)
{
var city = await _context.Cities.FindAsync(id);
if (city == null)
{
return NotFound();
}
return city;
}
// PUT: api/Cities/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task PutCity(int id, City city)
{
if (id != city.Id)
{
return BadRequest();
}
//var sourceCity = _context.Cities.Where(i => i.Id == city.Id).FirstOrDefault();
//if (sourceCity == null) return BadRequest();
//sourceCity.Name = city.Name;
//sourceCity.Lat = city.Lat;
//sourceCity.Lon = city.Lon;
_context.Entry(city).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CityExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Cities
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task> PostCity(City city)
{
_context.Cities.Add(city);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCity", new { id = city.Id }, city);
}
// DELETE: api/Cities/5
[HttpDelete("{id}")]
public async Task> DeleteCity(int id)
{
var city = await _context.Cities.FindAsync(id);
if (city == null)
{
return NotFound();
}
_context.Cities.Remove(city);
await _context.SaveChangesAsync();
return city;
}
private bool CityExists(int id)
{
return _context.Cities.Any(e => e.Id == id);
}
[HttpPost]
[Route("IsDupeCity")]
public bool IsDupeCity(City city)
{
return _context.Cities.Any(
e => e.Name == city.Name
&& e.Lat == city.Lat
&& e.Lon == city.Lon
&& e.CountryId == city.CountryId
&& e.Id != city.Id);
}
}
}
================================================
FILE: Chapter_06/WorldCities/Controllers/CountriesController.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using WorldCities.Data;
using WorldCities.Data.Models;
namespace WorldCities.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CountriesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public CountriesController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/Cities
// GET: api/Countries/?pageIndex=0&pageSize=10
// GET: api/Countries/?pageIndex=0&pageSize=10&sortColumn=name&sortOrder=asc
// GET: api/Countries/?pageIndex=0&pageSize=10&sortColumn=name&sortOrder=asc&filterColumn=name&filterQuery=york
[HttpGet]
public async Task>> GetCountries(
int pageIndex = 0,
int pageSize = 10,
string sortColumn = null,
string sortOrder = null,
string filterColumn = null,
string filterQuery = null)
{
return await ApiResult.CreateAsync(
_context.Countries,
pageIndex,
pageSize,
sortColumn,
sortOrder,
filterColumn,
filterQuery);
}
// GET: api/Countries/5
[HttpGet("{id}")]
public async Task> GetCountry(int id)
{
var country = await _context.Countries.FindAsync(id);
if (country == null)
{
return NotFound();
}
return country;
}
// PUT: api/Countries/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task PutCountry(int id, Country country)
{
if (id != country.Id)
{
return BadRequest();
}
_context.Entry(country).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CountryExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Countries
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task> PostCountry(Country country)
{
_context.Countries.Add(country);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCountry", new { id = country.Id }, country);
}
// DELETE: api/Countries/5
[HttpDelete("{id}")]
public async Task> DeleteCountry(int id)
{
var country = await _context.Countries.FindAsync(id);
if (country == null)
{
return NotFound();
}
_context.Countries.Remove(country);
await _context.SaveChangesAsync();
return country;
}
private bool CountryExists(int id)
{
return _context.Countries.Any(e => e.Id == id);
}
[HttpPost]
[Route("IsDupeField")]
public bool IsDupeField(
int countryId,
string fieldName,
string fieldValue)
{
// Standard approach(using strongly-typed LAMBA expressions)
//switch (fieldName)
//{
// case "name":
// return _context.Countries.Any(
// c => c.Name == fieldValue && c.Id != countryId);
// case "iso2":
// return _context.Countries.Any(
// c => c.ISO2 == fieldValue && c.Id != countryId);
// case "iso3":
// return _context.Countries.Any(
// c => c.ISO3 == fieldValue && c.Id != countryId);
// default:
// return false;
//}
// Dynamic approach (using System.Linq.Dynamic.Core)
return (ApiResult.IsValidProperty(fieldName, true))
? _context.Countries.Any(
String.Format("{0} == @0 && Id != @1", fieldName),
fieldValue,
countryId)
: false;
}
}
}
================================================
FILE: Chapter_06/WorldCities/Controllers/SeedController.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WorldCities.Data;
using OfficeOpenXml;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using WorldCities.Data.Models;
using System.Text.Json;
namespace WorldCities.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class SeedController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly IWebHostEnvironment _env;
public SeedController(
ApplicationDbContext context,
IWebHostEnvironment env)
{
_context = context;
_env = env;
}
[HttpGet]
public async Task Import()
{
// NOTE: This method has been updated on 2020.09.13.
// The new version is more efficient than the code described in the book's Chapter 4.
// ref.: https://github.com/PacktPublishing/ASP.NET-Core-3-and-Angular-9-Third-Edition/issues/15
var path = Path.Combine(
_env.ContentRootPath,
String.Format("Data/Source/worldcities.xlsx"));
using (var stream = new FileStream(
path,
FileMode.Open,
FileAccess.Read))
{
using (var ep = new ExcelPackage(stream))
{
// get the first worksheet
var ws = ep.Workbook.Worksheets[0];
// initialize the record counters
var nCountries = 0;
var nCities = 0;
#region Import all Countries
// create a list containing all the countries already existing
// into the Database (it will be empty on first run).
var lstCountries = _context.Countries.ToList();
// iterates through all rows, skipping the first one
for (int nRow = 2;
nRow <= ws.Dimension.End.Row;
nRow++)
{
var row = ws.Cells[nRow, 1, nRow, ws.Dimension.End.Column];
var name = row[nRow, 5].GetValue();
// does this country already exist in the database?
if (lstCountries.Where(c => c.Name == name).Count() == 0)
{
// create the Country entity and fill it with xlsx data
var country = new Country();
country.Name = name;
country.ISO2 = row[nRow, 6].GetValue();
country.ISO3 = row[nRow, 7].GetValue();
// add the new country to the DB context
_context.Countries.Add(country);
// store the country to retrieve its Id later on
lstCountries.Add(country);
// increment the counter
nCountries++;
}
}
// save all the countries into the Database
if (nCountries > 0) await _context.SaveChangesAsync();
#endregion
#region Import all Cities
// create a list containing all the cities already existing
// into the Database (it will be empty on first run).
var lstCities = _context.Cities.ToList();
// iterates through all rows, skipping the first one
for (int nRow = 2;
nRow <= ws.Dimension.End.Row;
nRow++)
{
var row = ws.Cells[nRow, 1, nRow, ws.Dimension.End.Column];
var name = row[nRow, 1].GetValue();
var name_ASCII = row[nRow, 2].GetValue();
var countryName = row[nRow, 5].GetValue();
var lat = row[nRow, 3].GetValue();
var lon = row[nRow, 4].GetValue();
// retrieve country and countryId
var country = lstCountries.Where(c => c.Name == countryName)
.FirstOrDefault();
var countryId = country.Id;
// does this city already exist in the database?
if (lstCities.Where(
c => c.Name == name
&& c.Lat == lat
&& c.Lon == lon
&& c.CountryId == countryId
).Count() == 0)
{
// create the City entity and fill it with xlsx data
var city = new City();
city.Name = name;
city.Name_ASCII = name_ASCII;
city.Lat = lat;
city.Lon = lon;
city.CountryId = countryId;
// add the new city to the DB context
_context.Cities.Add(city);
// increment the counter
nCities++;
}
}
// save all the cities into the Database
if (nCities > 0) await _context.SaveChangesAsync();
#endregion
return new JsonResult(new
{
Cities = nCities,
Countries = nCountries
});
}
}
}
}
}
================================================
FILE: Chapter_06/WorldCities/Data/ApiResult.cs
================================================
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Linq.Dynamic.Core;
using System.Reflection;
namespace WorldCities.Data
{
public class ApiResult
{
///
/// Private constructor called by the CreateAsync method.
///
private ApiResult(
List data,
int count,
int pageIndex,
int pageSize,
string sortColumn,
string sortOrder,
string filterColumn,
string filterQuery)
{
Data = data;
PageIndex = pageIndex;
PageSize = pageSize;
TotalCount = count;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
SortColumn = sortColumn;
SortOrder = sortOrder;
FilterColumn = filterColumn;
FilterQuery = filterQuery;
}
#region Methods
///
/// Pages, sorts and/or filters a IQueryable source.
///
/// An IQueryable source of generic type
/// Zero-based current page index (0 = first page)
/// The actual size of each page
/// The sorting colum name
/// The sorting order ("ASC" or "DESC")
/// The filtering column name
/// The filtering query (value to lookup)
///
/// A object containing the IQueryable paged/sorted/filtered result
/// and all the relevant paging/sorting/filtering navigation info.
///
public static async Task> CreateAsync(
IQueryable source,
int pageIndex,
int pageSize,
string sortColumn = null,
string sortOrder = null,
string filterColumn = null,
string filterQuery = null)
{
if (!String.IsNullOrEmpty(filterColumn)
&& !String.IsNullOrEmpty(filterQuery)
&& IsValidProperty(filterColumn))
{
source = source.Where(
String.Format("{0}.Contains(@0)",
filterColumn),
filterQuery);
}
var count = await source.CountAsync();
if (!String.IsNullOrEmpty(sortColumn)
&& IsValidProperty(sortColumn))
{
sortOrder = !String.IsNullOrEmpty(sortOrder)
&& sortOrder.ToUpper() == "ASC"
? "ASC"
: "DESC";
source = source.OrderBy(
String.Format(
"{0} {1}",
sortColumn,
sortOrder)
);
}
source = source
.Skip(pageIndex * pageSize)
.Take(pageSize);
var data = await source.ToListAsync();
return new ApiResult(
data,
count,
pageIndex,
pageSize,
sortColumn,
sortOrder,
filterColumn,
filterQuery);
}
///
/// Checks if the given property name exists
/// to protect against SQL injection attacks
///
public static bool IsValidProperty(
string propertyName,
bool throwExceptionIfNotFound = true)
{
var prop = typeof(T).GetProperty(
propertyName,
BindingFlags.IgnoreCase |
BindingFlags.Public |
BindingFlags.Static |
BindingFlags.Instance);
if (prop == null && throwExceptionIfNotFound)
throw new NotSupportedException(
String.Format(
"ERROR: Property '{0}' does not exist.",
propertyName)
);
return prop != null;
}
#endregion
#region Properties
///
/// IQueryable data result to return.
///
public List Data { get; private set; }
///
/// Zero-based index of current page.
///
public int PageIndex { get; private set; }
///
/// Number of items contained in each page.
///
public int PageSize { get; private set; }
///
/// Total items count
///
public int TotalCount { get; private set; }
///
/// Total pages count
///
public int TotalPages { get; private set; }
///
/// TRUE if the current page has a previous page, FALSE otherwise.
///
public bool HasPreviousPage
{
get
{
return (PageIndex > 0);
}
}
///
/// TRUE if the current page has a next page, FALSE otherwise.
///
public bool HasNextPage
{
get
{
return ((PageIndex +1) < TotalPages);
}
}
///
/// Sorting Column name (or null if none set)
///
public string SortColumn { get; set; }
///
/// Sorting Order ("ASC", "DESC" or null if none set)
///
public string SortOrder { get; set; }
///
/// Filter Column name (or null if none set)
///
public string FilterColumn { get; set; }
///
/// Filter Query string
/// (to be used within the given FilterColumn)
///
public string FilterQuery { get; set; }
#endregion
}
}
================================================
FILE: Chapter_06/WorldCities/Data/ApplicationDbContext.cs
================================================
using Microsoft.EntityFrameworkCore;
using WorldCities.Data.Models;
namespace WorldCities.Data
{
public class ApplicationDbContext : DbContext
{
#region Constructor
public ApplicationDbContext() : base()
{
}
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
#endregion Constructor
#region Methods
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Map Entity names to DB Table names
modelBuilder.Entity().ToTable("Cities");
modelBuilder.Entity().ToTable("Countries");
}
#endregion Methods
#region Properties
public DbSet Cities { get; set; }
public DbSet Countries { get; set; }
#endregion Properties
}
}
================================================
FILE: Chapter_06/WorldCities/Data/Migrations/20191123030140_Initial.Designer.cs
================================================
//
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using WorldCities.Data;
namespace WorldCities.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20191123030140_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("CountryId")
.HasColumnType("int");
b.Property("Lat")
.HasColumnType("decimal(7,4)");
b.Property("Lon")
.HasColumnType("decimal(7,4)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.Property("Name_ASCII")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("Cities");
});
modelBuilder.Entity("WorldCities.Data.Models.Country", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("ISO2")
.HasColumnType("nvarchar(max)");
b.Property("ISO3")
.HasColumnType("nvarchar(max)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.HasOne("WorldCities.Data.Models.Country", "Country")
.WithMany("Cities")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
================================================
FILE: Chapter_06/WorldCities/Data/Migrations/20191123030140_Initial.cs
================================================
using Microsoft.EntityFrameworkCore.Migrations;
namespace WorldCities.Data.Migrations
{
public partial class Initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Countries",
columns: table => new
{
Id = table.Column(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column(nullable: true),
ISO2 = table.Column(nullable: true),
ISO3 = table.Column(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Countries", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Cities",
columns: table => new
{
Id = table.Column(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column(nullable: true),
Name_ASCII = table.Column(nullable: true),
Lat = table.Column(type: "decimal(7,4)", nullable: false),
Lon = table.Column(type: "decimal(7,4)", nullable: false),
CountryId = table.Column(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Cities", x => x.Id);
table.ForeignKey(
name: "FK_Cities_Countries_CountryId",
column: x => x.CountryId,
principalTable: "Countries",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Cities_CountryId",
table: "Cities",
column: "CountryId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Cities");
migrationBuilder.DropTable(
name: "Countries");
}
}
}
================================================
FILE: Chapter_06/WorldCities/Data/Migrations/ApplicationDbContextModelSnapshot.cs
================================================
//
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using WorldCities.Data;
namespace WorldCities.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("CountryId")
.HasColumnType("int");
b.Property("Lat")
.HasColumnType("decimal(7,4)");
b.Property("Lon")
.HasColumnType("decimal(7,4)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.Property("Name_ASCII")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("Cities");
});
modelBuilder.Entity("WorldCities.Data.Models.Country", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property("ISO2")
.HasColumnType("nvarchar(max)");
b.Property("ISO3")
.HasColumnType("nvarchar(max)");
b.Property("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("WorldCities.Data.Models.City", b =>
{
b.HasOne("WorldCities.Data.Models.Country", "Country")
.WithMany("Cities")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
================================================
FILE: Chapter_06/WorldCities/Data/Models/City.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace WorldCities.Data.Models
{
public class City
{
#region Constructor
public City()
{
}
#endregion
#region Properties
///
/// The unique id and primary key for this City
///
[Key]
[Required]
public int Id { get; set; }
///
/// City name (in UTF8 format)
///
public string Name { get; set; }
///
/// City name (in ASCII format)
///
public string Name_ASCII { get; set; }
///
/// City latitude
///
[Column(TypeName = "decimal(7,4)")]
public decimal Lat { get; set; }
///
/// City longitude
///
[Column(TypeName = "decimal(7,4)")]
public decimal Lon { get; set; }
///
/// Country Id (foreign key)
///
[ForeignKey("Country")]
public int CountryId { get; set; }
#endregion
#region Navigation Properties
///
/// The country related to this city.
///
public virtual Country Country { get; set; }
#endregion
}
}
================================================
FILE: Chapter_06/WorldCities/Data/Models/Country.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace WorldCities.Data.Models
{
public class Country
{
#region Constructor
public Country()
{
}
#endregion
#region Properties
///
/// The unique id and primary key for this Country
///
[Key]
[Required]
public int Id { get; set; }
///
/// Country name (in UTF8 format)
///
public string Name { get; set; }
///
/// Country code (in ISO 3166-1 ALPHA-2 format)
///
[JsonPropertyName("iso2")]
public string ISO2 { get; set; }
///
/// Country code (in ISO 3166-1 ALPHA-3 format)
///
[JsonPropertyName("iso3")]
public string ISO3 { get; set; }
#endregion
#region Navigation Properties
///
/// A list containing all the cities related to this country.
///
public virtual List Cities { get; set; }
#endregion
}
}
================================================
FILE: Chapter_06/WorldCities/Pages/Error.cshtml
================================================
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
Error.
An error occurred while processing your request.
@if (Model.ShowRequestId)
{
Request ID:@Model.RequestId
}
Development Mode
Swapping to the Development environment displays detailed information about the error that occurred.
The Development environment shouldn't be enabled for deployed applications.
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
and restarting the app.
================================================
FILE: Chapter_06/WorldCities/Pages/Error.cshtml.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace WorldCities.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
private readonly ILogger _logger;
public ErrorModel(ILogger logger)
{
_logger = logger;
}
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
================================================
FILE: Chapter_06/WorldCities/Pages/_ViewImports.cshtml
================================================
@using WorldCities
@namespace WorldCities.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
================================================
FILE: Chapter_06/WorldCities/Program.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace WorldCities
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
}
}
================================================
FILE: Chapter_06/WorldCities/Startup.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Text.Json;
using WorldCities.Data;
namespace WorldCities
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
.AddJsonOptions(options => {
// set this option to TRUE to indent the JSON output
options.JsonSerializerOptions.WriteIndented = true;
// set this option to NULL to use PascalCase instead of CamelCase (default)
// options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
// Add EntityFramework support for SqlServer.
services.AddEntityFrameworkSqlServer();
// Add ApplicationDbContext.
services.AddDbContext(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")
)
);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
================================================
FILE: Chapter_06/WorldCities/WorldCities.csproj
================================================
netcoreapp3.1trueLatestfalseClientApp\$(DefaultItemExcludes);$(SpaRoot)node_modules\**falseallruntime; build; native; contentfiles; analyzers; buildtransitive%(DistFiles.Identity)PreserveNewesttrue
================================================
FILE: Chapter_06/WorldCities/appsettings.Development.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
================================================
FILE: Chapter_06/WorldCities/appsettings.json
================================================
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=WorldCities;User Id=WorldCities;Password=MyVeryOwn$721;Integrated Security=False;MultipleActiveResultSets=True"
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
================================================
FILE: Chapter_06.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29209.62
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorldCities", "Chapter_06\WorldCities\WorldCities.csproj", "{32CB8454-FCEC-48A0-8A57-064FCF714912}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{32CB8454-FCEC-48A0-8A57-064FCF714912}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32CB8454-FCEC-48A0-8A57-064FCF714912}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32CB8454-FCEC-48A0-8A57-064FCF714912}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32CB8454-FCEC-48A0-8A57-064FCF714912}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F0EE0686-783C-472C-8B14-22385361AEE1}
EndGlobalSection
EndGlobal
================================================
FILE: Chapter_07/WorldCities/.gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
bin/
Bin/
obj/
Obj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
orleans.codegen.cs
/node_modules
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
================================================
FILE: Chapter_07/WorldCities/ClientApp/.editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: Chapter_07/WorldCities/ClientApp/.gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
================================================
FILE: Chapter_07/WorldCities/ClientApp/README.md
================================================
# WorldCities
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: Chapter_07/WorldCities/ClientApp/angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"WorldCities": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"progress": true,
"extractCss": true,
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "WorldCities:build"
},
"configurations": {
"production": {
"browserTarget": "WorldCities:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "WorldCities:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"styles.css"
],
"scripts": [],
"assets": [
"src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist-server",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.server.json"
},
"configurations": {
"dev": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": true
},
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false
}
}
}
}
},
"WorldCities-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "WorldCities:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "WorldCities"
}
================================================
FILE: Chapter_07/WorldCities/ClientApp/browserslist
================================================
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
# IE 9-11
================================================
FILE: Chapter_07/WorldCities/ClientApp/e2e/protractor.conf.js
================================================
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require("jasmine-spec-reporter");
exports.config = {
allScriptsTimeout: 11000,
specs: ["./src/**/*.e2e-spec.ts"],
capabilities: {
browserName: "chrome"
},
directConnect: true,
baseUrl: "http://localhost:4200/",
framework: "jasmine",
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require("ts-node").register({
project: require("path").join(__dirname, "./tsconfig.e2e.json")
});
jasmine
.getEnv()
.addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
================================================
FILE: Chapter_07/WorldCities/ClientApp/e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getMainHeading()).toEqual('Hello, world!');
});
});
================================================
FILE: Chapter_07/WorldCities/ClientApp/e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getMainHeading() {
return element(by.css('app-root h1')).getText();
}
}
================================================
FILE: Chapter_07/WorldCities/ClientApp/e2e/tsconfig.e2e.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
================================================
FILE: Chapter_07/WorldCities/ClientApp/package.json
================================================
{
"name": "worldcities",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "echo Starting... && ng serve",
"build": "ng build",
"build:ssr": "ng run WorldCities:server:dev",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "9.0.0",
"@angular/cdk": "9.0.0",
"@angular/common": "9.0.0",
"@angular/compiler": "9.0.0",
"@angular/core": "9.0.0",
"@angular/forms": "9.0.0",
"@angular/material": "9.0.0",
"@angular/platform-browser": "9.0.0",
"@angular/platform-browser-dynamic": "9.0.0",
"@angular/platform-server": "9.0.0",
"@angular/router": "9.0.0",
"@nguniversal/module-map-ngfactory-loader": "9.0.0-next.9",
"aspnet-prerendering": "3.0.1",
"bootstrap": "4.4.1",
"core-js": "3.6.1",
"hammerjs": "2.0.8",
"jquery": "3.5.1",
"oidc-client": "1.9.1",
"popper.js": "1.16.0",
"rxjs": "6.5.4",
"zone.js": "0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.900.0",
"@angular/cli": "9.0.0",
"@angular/compiler-cli": "9.0.0",
"@angular/language-service": "9.0.0",
"@types/jasmine": "3.5.0",
"@types/jasminewd2": "2.0.8",
"@types/node": "13.1.1",
"codelyzer": "5.2.1",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "4.4.1",
"karma-chrome-launcher": "3.1.0",
"karma-coverage-istanbul-reporter": "2.1.1",
"karma-jasmine": "2.0.1",
"karma-jasmine-html-reporter": "1.5.1",
"typescript": "3.7.5"
},
"optionalDependencies": {
"node-sass": "4.13.0",
"protractor": "5.4.2",
"ts-node": "5.0.1",
"tslint": "5.20.1"
}
}
================================================
FILE: Chapter_07/WorldCities/ClientApp/src/app/angular-material.module.ts
================================================
import { NgModule } from '@angular/core';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
@NgModule({
imports: [
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatInputModule,
MatSelectModule
],
exports: [
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatInputModule,
MatSelectModule
]
})
export class AngularMaterialModule { }
================================================
FILE: Chapter_07/WorldCities/ClientApp/src/app/app.component.html
================================================
================================================
FILE: Chapter_07/WorldCities/ClientApp/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
title = 'app';
}
================================================
FILE: Chapter_07/WorldCities/ClientApp/src/app/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { BaseFormComponent } from './base.form.component';
import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { HomeComponent } from './home/home.component';
import { CitiesComponent } from './cities/cities.component';
import { CityEditComponent } from './cities/city-edit.component';
import { CountriesComponent } from './countries/countries.component';
import { CountryEditComponent } from './countries/country-edit.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AngularMaterialModule } from './angular-material.module';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent,
BaseFormComponent,
NavMenuComponent,
HomeComponent,
CitiesComponent,
CityEditComponent,
CountriesComponent,
CountryEditComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
HttpClientModule,
FormsModule,
RouterModule.forRoot([
{ path: '', component: HomeComponent, pathMatch: 'full' },
{ path: 'cities', component: CitiesComponent },
{ path: 'city/:id', component: CityEditComponent },
{ path: 'city', component: CityEditComponent },
{ path: 'countries', component: CountriesComponent },
{ path: 'country/:id', component: CountryEditComponent },
{ path: 'country', component: CountryEditComponent }
]),
BrowserAnimationsModule,
AngularMaterialModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
================================================
FILE: Chapter_07/WorldCities/ClientApp/src/app/app.server.module.ts
================================================
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { AppComponent } from './app.component';
import { AppModule } from './app.module';
@NgModule({
imports: [AppModule, ServerModule, ModuleMapLoaderModule],
bootstrap: [AppComponent]
})
export class AppServerModule { }
================================================
FILE: Chapter_07/WorldCities/ClientApp/src/app/base.form.component.ts
================================================
import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
template: ''
})
export class BaseFormComponent {
// the form model
form: FormGroup;
constructor() {
}
// retrieve a FormControl
getControl(name: string) {
return this.form.get(name);
}
// returns TRUE if the FormControl is valid
isValid(name: string) {
var e = this.getControl(name);
return e && e.valid;
}
// returns TRUE if the FormControl has been changed
isChanged(name: string) {
var e = this.getControl(name);
return e && (e.dirty || e.touched);
}
// returns TRUE if the FormControl is raising an error,
// i.e. an invalid state after user changes
hasError(name: string) {
var e = this.getControl(name);
return e && (e.dirty || e.touched) && e.invalid;
}
}
================================================
FILE: Chapter_07/WorldCities/ClientApp/src/app/base.service.ts
================================================
import { Injectable, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export abstract class BaseService {
constructor(
protected http: HttpClient,
protected baseUrl: string
) {
}
abstract getData(
pageIndex: number,
pageSize: number,
sortColumn: string,
sortOrder: string,
filterColumn: string,
filterQuery: string): Observable;
abstract get(id: number): Observable;
abstract put(item: T): Observable;
abstract post(item: T): Observable;
}
export interface ApiResult {
data: T[];
pageIndex: number;
pageSize: number;
totalCount: number;
totalPages: number;
sortColumn: string;
sortOrder: string;
filterColumn: string;
filterQuery: string;
}
================================================
FILE: Chapter_07/WorldCities/ClientApp/src/app/cities/cities.component.css
================================================
table {
width: 100%;
}
.mat-form-field {
font-size: 14px;
width: 100%;
}
================================================
FILE: Chapter_07/WorldCities/ClientApp/src/app/cities/cities.component.html
================================================
Cities
Here's a list of cities: feel free to play with it.