Before proceeding further, please read the following previous articles for updating .NET Core version to use Angular.

Getting Started

  • Start Visual Studio 2017.
  • Select "New Project".
  • Select .NET Core from Web template and select ASP.NET Core Web Application.
  • Provide a project name.
  • Provide project location to save and click OK.


    Image 1.

  • Select Angular from the given templates.
  • Select .NET Core version and click OK.


    Image 2.

  • Here is the structure of your project.


    Image 3.

Here is my database table schema structure diagram.


Image 4.

Now, let’s work on functionality part. First of all, add a connection string in appsettings.json.

Now, go to Tools >> NuGet Package Manager >> Package Manager Console now.


Image 5.

Add Scaffold DB Context command and provide the database connection string, output directory name, and table’s name.

Image 6.

Wait until the command executes. Once done, check in the Models folder.


Image 7.

As you can see 3 class are added which has all model classes and context in the Models folder.

First thing before going further, go in the dbcotext class and comment this line of code which has hardcoded connection string.

  1. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  2. {
  3. //if (!optionsBuilder.IsConfigured)
  4. //{
  5. //#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
  6. //optionsBuilder.UseSqlServer(@"Server=localhost;Database=NORTHWND;Trusted_Connection=True;");
  7. // }
  8. }

Now, go to the Startup.cs class and add the following line for connection string and to serialize JSON data.

  1. public void ConfigureServices(IServiceCollection services) {
  2. // Add framework services.
  3. services.AddDbContext < NORTHWNDContext > (options => options.UseSqlServer(Configuration.GetConnectionString("NORTHWNDConnString")));
  4. services.AddScoped < EmployeeDAL > ();
  5. services.AddMvc().AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
  6. }
Now, add a database access layer class in the Models folder.

EmployeeDAL.cs

  1. using Microsoft.EntityFrameworkCore;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. namespace secondsample.Models {
  7. public class EmployeeDAL {
  8. private readonly NORTHWNDContext _context;
  9. public EmployeeDAL(NORTHWNDContext context) {
  10. _context = context;
  11. }
  12. /// <summary>
  13. /// gets all employees list
  14. /// </summary>
  15. /// <returns></returns>
  16. public async Task < IEnumerable < Employees >> GetAllEmployees() {
  17. try {
  18. return await _context.Employees.ToListAsync();
  19. } catch {
  20. throw;
  21. }
  22. }
  23. /// <summary>
  24. /// add new employee
  25. /// </summary>
  26. /// <param name="employee"></param>
  27. /// <returns></returns>
  28. public int AddEmployee(Employees employee) {
  29. try {
  30. _context.Employees.Add(employee);
  31. _context.SaveChanges();
  32. return 1;
  33. } catch {
  34. throw;
  35. }
  36. }
  37. /// <summary>
  38. /// update an existing employee
  39. /// </summary>
  40. /// <param name="employee"></param>
  41. /// <returns></returns>
  42. public int UpdateEmployee(Employees employee) {
  43. try {
  44. _context.Entry(employee).State = EntityState.Modified;
  45. _context.SaveChanges();
  46. return 1;
  47. } catch {
  48. throw;
  49. }
  50. }
  51. /// <summary>
  52. /// get an employee detail
  53. /// </summary>
  54. /// <param name="EmployeeID"></param>
  55. /// <returns></returns>
  56. public Employees GetEmployee(int EmployeeID) {
  57. try {
  58. Employees employee = _context.Employees.Find(EmployeeID);
  59. return employee;
  60. } catch {
  61. throw;
  62. }
  63. }
  64. /// <summary>
  65. /// Delete an employee
  66. /// </summary>
  67. /// <param name="EmployeeID"></param>
  68. /// <returns></returns>
  69. public int DeleteEmployee(int EmployeeID) {
  70. try {
  71. Employees employee = _context.Employees.Find(EmployeeID);
  72. _context.Employees.Remove(employee);
  73. _context.SaveChanges();
  74. return 1;
  75. } catch {
  76. throw;
  77. }
  78. }
  79. }
  80. }
Add a new controller class in Controller folder and make all API functions and Routes.

EmployeeController.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Mvc;
  6. using secondsample.Models;
  7. namespace secondsample.Controllers {
  8. [Route("api/Employees")]
  9. public class EmployeeController: Controller {
  10. private readonly EmployeeDAL _employeeDAL;
  11. public EmployeeController(EmployeeDAL employeeDAL) {
  12. _employeeDAL = employeeDAL;
  13. }
  14. [HttpGet]
  15. [Route("GetEmployees")]
  16. public async Task < IEnumerable < Employees >> GetEmployees() {
  17. return await _employeeDAL.GetAllEmployees();
  18. }
  19. [HttpPost]
  20. [Route("AddEmployee")]
  21. public int AddEmployee([FromBody] Employees employee) {
  22. return _employeeDAL.AddEmployee(employee);
  23. }
  24. [HttpGet]
  25. [Route("GetEmployeeDetail/{employeeID}")]
  26. public Employees GetEmployeeDetail(int employeeID) {
  27. return _employeeDAL.GetEmployee(employeeID);
  28. }
  29. [HttpPut]
  30. [Route("EditEmployee")]
  31. public int EditEmployee([FromBody] Employees employee) {
  32. return _employeeDAL.UpdateEmployee(employee);
  33. }
  34. [HttpDelete]
  35. [Route("DeleteEmployee/{EmployeeID}")]
  36. public int DeleteEmployee(int EmployeeID) {
  37. return _employeeDAL.DeleteEmployee(EmployeeID);
  38. }
  39. }
  40. }
We are done with database layer class and API controller class, now let’s work on Angular part.

Add a new services folder in app folder which is in ClientApp directory, add a new TypeScript file, and add the following code.

employeeservices.ts

  1. import {
  2. Injectable,
  3. Inject
  4. } from '@angular/core';
  5. import {
  6. Http,
  7. Response
  8. } from '@angular/http';
  9. import {
  10. Observable
  11. } from 'rxjs/Observable';
  12. import {
  13. Router
  14. } from '@angular/router';
  15. import 'rxjs/add/operator/map';
  16. import 'rxjs/add/operator/catch';
  17. import 'rxjs/add/observable/throw';
  18. @Injectable()
  19. export class EmployeeService {
  20. myAppUrl: string = "";
  21. constructor(private _http: Http, @Inject('BASE_URL') baseUrl: string) {
  22. this.myAppUrl = baseUrl;
  23. }
  24. getEmployees() {
  25. return this._http.get(this.myAppUrl + 'api/Employees/GetEmployees').map((response: Response) => response.json()).catch(this.errorHandler);
  26. }
  27. getEmployeeById(EmployeeID: number) {
  28. return this._http.get(this.myAppUrl + "api/Employees/GetEmployeeDetail/" + EmployeeID).map((response: Response) => response.json()).catch(this.errorHandler)
  29. }
  30. deleteEmployee(EmployeeID: number) {
  31. return this._http.delete(this.myAppUrl + "api/Employees/DeleteEmployee/" + EmployeeID).map((response: Response) => response.json()).catch(this.errorHandler);
  32. }
  33. updateEmployee(employee: any) {
  34. return this._http.put(this.myAppUrl + 'api/Employees/EditEmployee', employee).map((response: Response) => response.json()).catch(this.errorHandler);
  35. }
  36. saveEmployee(employee: any) {
  37. return this._http.post(this.myAppUrl + 'api/Employees/AddEmployee', employee).map((response: Response) => response.json()).catch(this.errorHandler)
  38. }
  39. errorHandler(error: Response) {
  40. console.log(error);
  41. return Observable.throw(error.json().error || 'Server error');
  42. }
  43. }
Add two new folders in components folder - employeedata and addemployee and add TS components and HTML files in those folders.

employeedata.components.ts

  1. import {
  2. Component,
  3. Inject
  4. } from '@angular/core';
  5. import {
  6. Http,
  7. Headers
  8. } from '@angular/http';
  9. import {
  10. EmployeeService
  11. } from '../../services/employeeservice'
  12. import {
  13. Router,
  14. ActivatedRoute
  15. } from '@angular/router';
  16. @Component({
  17. selector: 'employeedata',
  18. templateUrl: './employeedata.component.html'
  19. })
  20. export class EmployeeDataComponent {
  21. public employeelist: EmployeeList[];
  22. constructor(public http: Http, private _router: Router, private _employeeService: EmployeeService) {
  23. this.getEmployees();
  24. }
  25. getEmployees() {
  26. this._employeeService.getEmployees().subscribe(data => this.employeelist = data)
  27. console.log(this.employeelist);
  28. }
  29. deleteEmployee(employeeId: number) {
  30. var ans = confirm("Do you want to delete employee with Id: " + employeeId);
  31. if (ans) {
  32. this._employeeService.deleteEmployee(employeeId).subscribe((data) => {
  33. this.getEmployees();
  34. }, error => console.error(error))
  35. }
  36. }
  37. }
  38. interface EmployeeList {
  39. EmployeeID: number;
  40. LastName: string;
  41. FirstName: string;
  42. Title: string;
  43. Address: string;
  44. City: string;
  45. Region: string;
  46. PostalCode: string;
  47. Country: string;
  48. Notes: string;
  49. }
employeedata.component.html
  1. <h1>Employee Data</h1>
  2. <p>This component demonstrates fetching Employee data from the server.</p>
  3. <p *ngIf="!employeelist"><em>Loading...</em></p>
  4. <p> <a [routerLink]="['/add-employee']">Create New</a> </p>
  5. <table class='table table-bordered' *ngIf="employeelist">
  6. <thead style="background-color:green;font-weight:bold;color:white;">
  7. <tr>
  8. <th>EmployeeID</th>
  9. <th>Name</th>
  10. <th>Title</th>
  11. <th>City</th>
  12. <th>Region</th>
  13. <th>Zip</th>
  14. <th>Country</th>
  15. <th>Notes</th>
  16. <th>Actions</th>
  17. </tr>
  18. </thead>
  19. <tbody>
  20. <tr *ngFor="let emp of employeelist">
  21. <td>{{ emp.employeeId }}</td>
  22. <td>{{ emp.lastName }}, {{ emp.firstName }}</td>
  23. <td>{{ emp.title }}</td>
  24. <td>{{ emp.city }}</td>
  25. <td>{{ emp.region }}</td>
  26. <td>{{ emp.postalCode }}</td>
  27. <td>{{ emp.country }}</td>
  28. <td>{{ emp.notes }}</td>
  29. <td> <a [routerLink]="['/employee/edit/', emp.employeeId]">Edit</a> | <a [routerLink]="" (click)="deleteEmployee(emp.employeeId)">Delete</a> </td>
  30. </tr>
  31. </tbody>
  32. </table>
addemployee.component.ts
  1. import {
  2. Component,
  3. OnInit
  4. } from '@angular/core';
  5. import {
  6. Http,
  7. Headers
  8. } from '@angular/http';
  9. import {
  10. NgForm,
  11. FormBuilder,
  12. FormGroup,
  13. Validators,
  14. FormControl
  15. } from '@angular/forms';
  16. import {
  17. Router,
  18. ActivatedRoute
  19. } from '@angular/router';
  20. import {
  21. EmployeeDataComponent
  22. } from '../employeedata/employeedata.component';
  23. import {
  24. EmployeeService
  25. } from '../../services/employeeservice';
  26. @Component({
  27. templateUrl: './addemployee.component.html'
  28. })
  29. export class AddEmployee implements OnInit {
  30. employeeForm: FormGroup;
  31. title: string = "Create";
  32. employeeId: number;
  33. errorMessage: any;
  34. constructor(private _fb: FormBuilder, private _avRoute: ActivatedRoute, private _employeeService: EmployeeService, private _router: Router) {
  35. if (this._avRoute.snapshot.params["employeeID"]) {
  36. this.employeeId = this._avRoute.snapshot.params["employeeID"];
  37. //alert(this.employeeId);
  38. }
  39. this.employeeForm = this._fb.group({
  40. employeeId: 0,
  41. firstName: ['', [Validators.required]],
  42. lastName: ['', [Validators.required]],
  43. title: ['', [Validators.required]],
  44. city: ['', [Validators.required]],
  45. region: ['', [Validators.required]],
  46. postalCode: ['', [Validators.required]],
  47. country: ['', [Validators.required]],
  48. notes: ['', [Validators.required]],
  49. titleOfCourtesy: ['', []],
  50. birthDate: ['', []],
  51. hireDate: ['', []],
  52. address: ['', []],
  53. homePhone: ['', []],
  54. extension: ['', []],
  55. photo: ['', []],
  56. reportsTo: ['', []],
  57. photoPath: ['', []],
  58. salary: ['', []],
  59. reportsToNavigation: ['', []],
  60. employeeTerritories: ['', []],
  61. inverseReportsToNavigation: ['', []]
  62. })
  63. }
  64. ngOnInit() {
  65. if (this.employeeId > 0) {
  66. this.title = "Edit";
  67. this._employeeService.getEmployeeById(this.employeeId).subscribe(resp => this.employeeForm.setValue(resp), error => this.errorMessage = error);
  68. }
  69. }
  70. save() {
  71. if (!this.employeeForm.valid) {
  72. return;
  73. }
  74. if (this.title == "Create") {
  75. this._employeeService.saveEmployee(this.employeeForm.value).subscribe((data) => {
  76. this._router.navigate(['/employee-data']);
  77. }, error => this.errorMessage = error)
  78. } else if (this.title == "Edit") {
  79. this._employeeService.updateEmployee(this.employeeForm.value).subscribe((data) => {
  80. this._router.navigate(['/employee-data']);
  81. }, error => this.errorMessage = error)
  82. }
  83. }
  84. cancel() {
  85. this._router.navigate(['/employee-data']);
  86. }
  87. get firstName() {
  88. return this.employeeForm.get('firstName');
  89. }
  90. get lastName() {
  91. return this.employeeForm.get('lastName');
  92. }
  93. get title1() {
  94. return this.employeeForm.get('title');
  95. }
  96. get city() {
  97. return this.employeeForm.get('city');
  98. }
  99. get region() {
  100. return this.employeeForm.get('region');
  101. }
  102. get postalCode() {
  103. return this.employeeForm.get('postalCode');
  104. }
  105. get country() {
  106. return this.employeeForm.get('country');
  107. }
  108. get notes() {
  109. return this.employeeForm.get('notes');
  110. }
  111. get titleOfCourtesy() {
  112. return this.employeeForm.get('titleOfCourtesy');
  113. }
  114. get birthDate() {
  115. return this.employeeForm.get('birthDate');
  116. }
  117. get hireDate() {
  118. return this.employeeForm.get('hireDate');
  119. }
  120. get address() {
  121. return this.employeeForm.get('address');
  122. }
  123. get homePhone() {
  124. return this.employeeForm.get('homePhone');
  125. }
  126. get extension() {
  127. return this.employeeForm.get('extension');
  128. }
  129. get photo() {
  130. return this.employeeForm.get('photo');
  131. }
  132. get reportsTo() {
  133. return this.employeeForm.get('reportsTo');
  134. }
  135. get photoPath() {
  136. return this.employeeForm.get('photoPath');
  137. }
  138. get salary() {
  139. return this.employeeForm.get('salary');
  140. }
  141. get reportsToNavigation() {
  142. return this.employeeForm.get('reportsToNavigation');
  143. }
  144. get employeeTerritories() {
  145. return this.employeeForm.get('employeeTerritories');
  146. }
  147. get inverseReportsToNavigation() {
  148. return this.employeeForm.get('inverseReportsToNavigation');
  149. }
  150. }
addemployee.component.html
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title></title>
  6. </head>
  7. <body>
  8. <h1>{{title}}</h1>
  9. <h3>Employee</h3>
  10. <hr />
  11. <form [formGroup]="employeeForm" (ngSubmit)="save()" #formDir="ngForm" novalidate>
  12. <div class="form-group row"> <label class=" control-label col-md-12" for="FirstName">First Name</label>
  13. <div class="col-md-4"> <input class="form-control" type="text" formControlName="firstName"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'firstName') && formDir.submitted">
  14. First Name is required.
  15. </span> </div>
  16. <div class="form-group row"> <label class=" control-label col-md-12" for="LastName">Last Name</label>
  17. <div class="col-md-4"> <input class="form-control" type="text" formControlName="lastName"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'lastName') && formDir.submitted">
  18. Last Name is required.
  19. </span> </div>
  20. <div class="form-group row"> <label class="control-label col-md-12" for="Title">Title</label>
  21. <div class="col-md-4"> <input class="form-control" type="text" formControlName="title"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'title') && formDir.submitted">
  22. Title is required
  23. </span> </div>
  24. <div class="form-group row"> <label class="control-label col-md-12" for="City">City</label>
  25. <div class="col-md-4"> <input class="form-control" type="text" formControlName="city"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'city') && formDir.submitted">
  26. City is required
  27. </span> </div>
  28. <div class="form-group row"> <label class="control-label col-md-12" for="Region">Region</label>
  29. <div class="col-md-4"> <input class="form-control" type="text" formControlName="region"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'region') && formDir.submitted">
  30. Region is required
  31. </span> </div>
  32. <div class="form-group row"> <label class="control-label col-md-12" for="postalCode">Zip</label>
  33. <div class="col-md-4"> <input class="form-control" type="text" formControlName="postalCode"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'postalCode') && formDir.submitted">
  34. Zip is required
  35. </span> </div>
  36. <div class="form-group row"> <label class="control-label col-md-12" for="Country">Country</label>
  37. <div class="col-md-4"> <input class="form-control" type="text" formControlName="country"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'country') && formDir.submitted">
  38. Country is required
  39. </span> </div>
  40. <div class="form-group row"> <label class="control-label col-md-12" for="Notes">Notes</label>
  41. <div class="col-md-4"> <textarea class="form-control" type="text" formControlName="notes"></textarea> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'notes') && formDir.submitted">
  42. Notes is required
  43. </span> </div>
  44. <div class="form-group"> <button type="submit" class="btn btn-default">Save</button> <button class="btn" (click)="cancel()">Cancel</button> </div>
  45. </form>
  46. </body>
  47. </html>
app.shared.module.ts
  1. import {
  2. NgModule
  3. } from '@angular/core';
  4. import {
  5. EmployeeService
  6. } from './services/employeeservice'
  7. import {
  8. CommonModule
  9. } from '@angular/common';
  10. import {
  11. FormsModule,
  12. ReactiveFormsModule
  13. } from '@angular/forms';
  14. import {
  15. HttpModule
  16. } from '@angular/http';
  17. import {
  18. RouterModule
  19. } from '@angular/router';
  20. import {
  21. AppComponent
  22. } from './components/app/app.component';
  23. import {
  24. NavMenuComponent
  25. } from './components/navmenu/navmenu.component';
  26. import {
  27. HomeComponent
  28. } from './components/home/home.component';
  29. import {
  30. FetchDataComponent
  31. } from './components/fetchdata/fetchdata.component';
  32. import {
  33. CounterComponent
  34. } from './components/counter/counter.component';
  35. import {
  36. EmployeeDataComponent
  37. } from './components/employeedata/employeedata.component';
  38. import {
  39. AddEmployee
  40. } from './components/addemployee/addemployee.component';
  41. @NgModule({
  42. declarations: [
  43. AppComponent,
  44. NavMenuComponent,
  45. CounterComponent,
  46. FetchDataComponent,
  47. EmployeeDataComponent,
  48. HomeComponent,
  49. AddEmployee
  50. ],
  51. imports: [
  52. CommonModule,
  53. HttpModule,
  54. FormsModule,
  55. ReactiveFormsModule,
  56. RouterModule.forRoot([{
  57. path: '',
  58. redirectTo: 'home',
  59. pathMatch: 'full'
  60. }, {
  61. path: 'home',
  62. component: HomeComponent
  63. }, {
  64. path: 'counter',
  65. component: CounterComponent
  66. }, {
  67. path: 'fetch-data',
  68. component: FetchDataComponent
  69. }, {
  70. path: 'employee-data',
  71. component: EmployeeDataComponent
  72. }, {
  73. path: 'add-employee',
  74. component: AddEmployee
  75. }, {
  76. path: 'employee/edit/:employeeID',
  77. component: AddEmployee
  78. }, {
  79. path: '**',
  80. redirectTo: 'home'
  81. }])
  82. ],
  83. providers: [EmployeeService]
  84. })
  85. export class AppModuleShared {}
At last, if you want to add a new navigation tab, then go to in app folder and navmenu >> navmenu.component.html and add whatever name you like to add.
  1. <div class='main-nav'>
  2. <div class='navbar navbar-inverse'>
  3. <div class='navbar-header'> <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
  4. <span class='sr-only'>Toggle navigation</span>
  5. <span class='icon-bar'></span>
  6. <span class='icon-bar'></span>
  7. <span class='icon-bar'></span>
  8. </button> <a class='navbar-brand' [routerLink]="['/home']">CRUD Operations in .NET Core with Angular Sample by Raj</a> </div>
  9. <div class='clearfix'></div>
  10. <div class='navbar-collapse collapse'>
  11. <ul class='nav navbar-nav'>
  12. <li [routerLinkActive]="['link-active']"> <a [routerLink]="['/home']">
  13. <span class='glyphicon glyphicon-home'></span> Home
  14. </a> </li>
  15. <li [routerLinkActive]="['link-active']"> <a [routerLink]="['/employee-data']">
  16. <span class='glyphicon glyphicon-th-list'></span> Employees
  17. </a> </li>
  18. </ul>
  19. </div>
  20. </div>
  21. </div>
So we are done here. Now, let’s run the project to see output.

Image 8.

As you can see when click on Employees tab then data is loading in the grid, that means, our data is coming correct and functionality is working. Now, let’s click on Edit link to edit existing record.


Image 9.

You can edit whatever information you like to edit and click on save button. Now, let’s create a new record.


Image 10.

You can delete any existing record on click on Delete link.

I don’t want to make this article bigger, so I am wrapping up here. I will upload source code soon and will share the link.

Conclusion

In this article, we learned how to build CRUD functionality with .NET Core angular and Entity Framework with SQL Server database.