Click here to Skip to main content
14,266,194 members

CRUD in ASP.NETCore MVC with Angular 2 and Web API

Rate this:
4.78 (34 votes)
Please Sign up or sign in to vote.
4.78 (34 votes)
16 Jan 2017CPOL
A good example of how build a CRUD web application using ASP.NETCORE with Angular2 and WebApi

Introduction

Through this article, you will learn how you can create a CRUD web application.

CRUD application means an application that can Create, Read, Update and Delete records on a Data Source (such as a Database, XML File, etc.).

At the end of this demo, you will learn how to:

  • Create an ASP.NETCore MVC application
  • Install required packages (Angular2, Typings, etc.) using npm
  • Reverse engineering of an existing database installed on SQL Server (using Entity Framework First Approach)
  • Create a RestFul Server using WebApi
  • Create components, template, services, and classes on Angular 2
  • Compile Angular2 project using webpack
  • Run the ASP.NETCORE MVC web application

In this article, I relied on the following links to build my application:

Background

To better understand this demo, it is preferable that you have a good knowledge’s of:

  • Programming in C#, JavaScript and HTML
  • MVC architecture
  • SQL language
  • Data Binding
  • Entity Framework
  • Visual Studio Code

Prerequisites

To run this example, you need to install:

Using the Code

Building the Web Application

In this section, I will proceed step by step to explain to you how you can easily build a CRUD web application:

A) Setup ASP.NETCore MVC Project

Open your CMD console (I recommend you to execute it in Administrator mode) and write the following commands:

  • mkdir dotnetcoreapp
  • cd dotnetcoreapp
  • dotnet new -t web to create web application
  • dotnet restore to load dependencies
  • dotnet run to start application

Result:

Use your browser to navigate to the given url (http://localhost:5000), you should see:

B) Create and Configure Angular2 Project

Based on the official Angular2 documentation, you will create the same configuration files:

  • package.json
  • tsconfig.json
  • typings.json

Next, you should install typescript, typings and webpack using npm:

  • Npm install –g typescript
  • Npm install -g typings
  • Npm install -g webpack

Finally, you will introduce some change in the Startup.cs file to support Angular2 Single Page Application, you should change the default ASP.NETCore MVC routing to point on the index page of the Angular2 project (wwwroot/index.html). So you will change the Configure method a little bit specially on routing code:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
     
           if (env.IsDevelopment())
            {
                //read error details
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            } 
//point on the index page of the Angular2 project
           app.Use(async (context, next) =>
            {
                await next();

                if (context.Response.StatusCode == 404
                    && !Path.HasExtension(context.Request.Path.Value))
                {
                    context.Request.Path = "/index.html";
                    await next();
                }
            }); 
            app.UseStaticFiles();
            app.UseMvc();
        }

C) Setup DataBase

1) Create Database

I used SQL Server 2014 to locally host my database.

The following steps will help you to prepare your database:

  • Add a new database named DataBaseDotnetCore to your server
  • Execute the following SQL Script to create a not empty table named Product
    CREATE TABLE [dbo].[Product](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [_name] [varchar](250) NULL,
    [_description] [varchar](250) NULL,
    Primary key(id),
    );
    
    insert into Product values ('Juice','Juice description'), _
    ('Orange','Orange description')
    
2) Using Entity Framework First Approach

In this section, you will do a reverse engineering to create an Entity Framework model from your existing database. To do that, you should follow the steps given below:

  • Navigate to the root folder of application (dotnetcoreapp folder)
  • Import required dependencies and tools: to do that, you should configure the project.json by:
    1. Adding the following dependencies:
      "Microsoft.EntityFrameworkCore":"1.0.0",
      "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
      "Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0",
      "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
      
    2. Adding tools:
      "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
    3. Saving the changes and executing the command line:
    4. dotnet -restore
  • Write the following command-line to start the process of reverse engineering:
    dotnet ef dbcontext scaffold "Server=[serverName];Database=[databaseName];
    Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models

    For example:

    dotnet ef dbcontext scaffold "Server=LFRUL-013;Database=DataBaseDotnetCore;
    Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models
  • Finally, modify the ConfigureServices method on Startup.cs file to create a connection to your database:
    public void ConfigureServices(IServiceCollection services)
    {            
        var connection = @"Server=LFRUL-013;Database=DataBaseDotnetCore;Trusted_Connection=True;";
        services.AddDbContext<DataBaseDotnetCoreContext>(options => options.UseSqlServer(connection));
        services.AddMvc();
    }

D) Setup Restful WebApi

In this step, you will create a controller named ProductsController.cs that implements some HTTP verbs:

  • Get: to retrieve the list of product from database
  • Post: to add a new Product
  • Put: to update a specific Product
  • Delete: to remove a specific Product
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using angularapp.Models;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace WebApplication
{
    [Route("api/[controller]")]
    [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true, Duration = -1)] 
    public class ProductsController : Controller
    {
        private DataBaseDotnetCoreContext _context; 

           public ProductsController(DataBaseDotnetCoreContext context)
        {
            _context = context;
        }

        [HttpGet]
        public IEnumerable<dynamic> Get()
        {
                return _context.Product.ToList();
        }

        [HttpPost]
        public string Post([FromBody] Product product)
        {
            Response.StatusCode = 200;
    
            try{
                    angularapp.Models.Product newProduct = new Product();
                    newProduct.Name = product.Name; 
                    newProduct.Description= product.Description; 
                     _context.Product.Add(newProduct);
                     _context.SaveChanges();
              
            }catch(Exception e){
                Response.StatusCode = 400;
                return e.ToString();
            }
            return "OK";
        }
        [HttpPut]
        public string Put([FromBody] Product product)
        {
            Response.StatusCode = 200;
    
            try{
                   
                    product.Name = product.Name; 
                    product.Description= product.Description; 
                     _context.Product.Attach(product);
                      _context.Entry(product).State = EntityState.Modified;
                     _context.SaveChanges();
              
            }catch(Exception e){
                Response.StatusCode = 400;
                return e.ToString();
            }
            return "OK";
        }

        [HttpDelete]
        public String Delete(int id)
        {
            Response.StatusCode = 200;
              
            try{
                    angularapp.Models.Product newProduct = new Product();
                    newProduct.Id = id; 
                    _context.Product.Remove(newProduct);
                     _context.SaveChanges();
        
            }catch(Exception e){
                 return e.ToString();
            }
            return "OK";
        }
    }
}

E) Setup Angular2 Project (frontend part)

In the wwwroot/app folder, create the following files:

a) product.service.ts

This is our service class that implements needed methods and interfaces such as:

  • AnnounceChange method: sends a notification to observers to call refresh data into a list of view,
  • LoadData method: loads data from database, thanks to the call of an existing web service [/api/products],
  • Add method: invokes an external web service [/api/products] that add a new Product object into database,
  • Update method: updates some content of an existing product into database by calling an existing web service [/api/products],
  • Delete method: deletes a specific product from database by calling an existing web service [/api/products],
  • IProduct class: constitutes an interface for Product class.
import { Injectable } from '@angular/core';
import { Http, Response, RequestOptions, Headers } from '@angular/http';
import { Observable, Subject } from 'rxjs/Rx';
import 'rxjs/add/operator/toPromise';

@Injectable()
export class ProductService {
    constructor(private _http: Http) { }
    private RegenerateData = new Subject<number>();
    // Observable string streams
    RegenerateData$ = this.RegenerateData.asObservable();
   
    AnnounceChange(mission: number) {
            
          this.RegenerateData.next(mission);
    }
    
    LoadData(): Promise<iproduct[]> {
        return this._http.get('/api/products')
            .toPromise()
            .then(response => this.extractArray(response))
            .catch(this.handleErrorPromise);
    }    

    Add(model) {
        let headers = new Headers({ 'Content-Type': 
        'application/json; charset=utf-8' });
        let options = new RequestOptions({ headers: headers });
        delete model["id"];
        let body = JSON.stringify(model);
        return this._http.post('/api/products/', body, 
               options).toPromise().catch(this.handleErrorPromise);
    }
    Update(model) {
        let headers = new Headers({ 'Content-Type': 
        'application/json; charset=utf-8' });
        let options = new RequestOptions({ headers: headers });
        let body = JSON.stringify(model);
        return this._http.put('/api/products/', body, 
               options).toPromise().catch(this.handleErrorPromise);
    }
    Delete(id: number) {  
        return this._http.delete('/api/products/?id=' + 
        id).toPromise().catch(this.handleErrorPromise);
    }    

    protected extractArray(res: Response, showprogress: boolean = true) {
        let data = res.json();
        
        return data || [];
    }

    protected handleErrorPromise(error: any): Promise<void> {
        try {
            error = JSON.parse(error._body);
        } catch (e) {
        }

        let errMsg = error.errorMessage
            ? error.errorMessage
            : error.message
                ? error.message
                : error._body
                    ? error._body
                    : error.status
                        ? `${error.status} - ${error.statusText}`
                        : 'unknown server error';

        console.error(errMsg);
        return Promise.reject(errMsg);
    }
}
export interface IProduct {
     id : number  ,      
     name : string ,      
     description : string
}
b) app.componentHW.ts

This is the core of the component. It contains the template and implementation of the application:

  • refresh method: Refreshes the existing list of product view by receiving data from an external service by calling "loadData method" of "_service variable" ,
  • onUpdate method: Updates an existing product on the database by calling "Update method" of "_service variable",
  • onDelete method: Deletes an existing product identified by its "unique key" by calling "Delete method" of "_service variable" .
import { Component, OnInit } from '@angular/core';
import { ProductService, IProduct } from './product.service';
import { ProductForm } from './productForm';
import { Subscription }   from 'rxjs/Subscription';
@Component({
  selector: 'myhw',
  template: `
      <div class='row'>
        <pform></pform>
      </div>
      <div class='row'>
       <div class="panel panel-default">
        <!-- Default panel contents -->
        <div class='panel-heading'>Products List</div>
        <div class='panel-body'>
          <table class='table table-condensed'>
            <thead>
              <th>Id</th>
              <th>Name</th>
              <th>Description</th>
                <th></th>
            </thead>
             <tbody>
              <tr *ngFor="let person of persons"  >
                  <td> {{person.id}}  </td>
                  <td> <input type="text"  
                  [(ngModel)]="person.name" 
                                            name="pname" 
                                            class="form-control" /> </td>
                  <td> <input type="text"  
                  [(ngModel)]="person.description" 
                                            name="pdescription"  
                                            class="form-control" /> </td>
                  <td> <input type="button" 
                  value="update" class="btn btn-default" 
                         (click)="onUpdate(person)"/> 
                         <input type="button" value="remove" 
                          class="btn btn-danger"  
                          (click)="onDelete(person.id)"/></td>
                </tr> 
            <tbody>
           </table>
           </div>
           </div>
        </div>
        `     
      })
export class HwComponent extends OnInit {
    subscription: Subscription;
    
     refresh(){
         this._service.loadData().then(data => {
            this.persons = data;
        })
    }
    constructor(private _service: ProductService) {
        super();
          this.subscription = _service.RegenerateData$.subscribe(
          mission => {
              console.log("Good !! ", mission);
               this.refresh();
           });
    }

    ngOnInit() {
        this.Refresh();
    }
    onUpdate(elem){
        console.log(elem); 
        this._service.Update(elem).then(data => {
         })
    }
    onDelete(elem : number){
        console.log("Delete Form ! ");
        console.log(elem); 
        this._service.Delete(elem).then(data => {
              this.Refresh();
        })
    }
    persons: IProduct[] = [];

     ngOnDestroy() {
    // prevent memory leak when component destroyed
       this.subscription.unsubscribe();
     }
}
c) Product.ts

This class contains details for product item.

export class Product {
      constructor(
        public id : number,      
        public name : string,      
        public description : string
      ){

      }
}
d) productForm.component.html

This is the HTML template used for our Product Form.

<div>
   <h3>Product Form</h3>
   <form>
     <div class="form-group">
       <label for="name">Name *</label>
       <input type="text" class="form-control"
       [(ngModel)]="model.name" name="name" required>
     </div>
     <div class="form-group">
       <label for="description">Description *</label>
       <input type="text" class="form-control"
       [(ngModel)]="model.description"  name="email">
     </div>
     <button type="button"  (click)="onSubmit()"
     class="btn btn-primary">Add</button>
   </form>
 </div>
e) productForm.ts

Constitute the code behind for our productForm template, in which you will have the implementation of:

  • onSumbit event: This event is used to invoke the Add service method
  • model attribute: That binds with the field of the Form
import { Component, OnInit } from '@angular/core';
import { Product } from './Product';
import { ProductService, IProduct } from './product.service';

@Component({
    moduleId: __filename,
    selector: 'pform',
    templateUrl: './app/productForm.component.html'    
 })

export class ProductForm {
    constructor(private _service: ProductService) {
     
    }
    model = new Product(0,'','');
    submitted = false;
    onSubmit() { 
        console.log("Sumbitted Form ! ");
        this.submitted = true; 
        this._service.Add(this.model).then(data => {
           this._service.AnnounceChange(1212);
        })
    }
    
  // TODO: Remove this when we're done
    get diagnostic() { return JSON.stringify(this.model); }
}
f) app.module.ts

This file will be used to:

  • import needed Angular2 modules via the word key: "imports"
  • declare components via the word key: "declarations"
  • declare services via the word key: "providers"
  • specify the root components to include into index.html file via the word key : "bootstrap"
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { HwComponent }   from './app.componentHW';
import { ProductService } from './product.service';
import {ProductForm } from './productForm';
 
@NgModule({
  imports:      [ BrowserModule,
                  FormsModule,
                  HttpModule],
  declarations: [ HwComponent, ProductForm],
  providers: [
        ProductService
  ],
  bootstrap:    [  HwComponent]
})
export class AppModule { }

Navigate to wwwroot folder in which you will start the creation of the frontend project:

a) systemjs.config.js

This file is used to load modules compiled using the TypeScript compiler.

/**
 * System configuration for Angular 2 samples
 * Adjust as necessary for your application needs.
 */
(function (global) {
    System.config({
        paths: {
            // paths serve as alias
            'npm:': '../node_modules/'
        },
        // map tells the System loader where to look for things
        map: {
            // our app is within the app folder
            app: 'app',
            // angular bundles
            '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
            '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
            '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
            '@angular/platform-browser': 
                 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
            '@angular/platform-browser-dynamic': 
                 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
            '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
            '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
            '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
            // other libraries
            'rxjs': 'npm:rxjs',
        },
        meta: {
            './app/main.js': {
                format: 'global'
            }
        },
        // packages tells the System loader how to load when no filename and/or no extension
        packages: {
            app: {
                main: './main.js',
                defaultExtension: 'js'
            },
            rxjs: {
                defaultExtension: 'js'
            }
        }
    });
})(this); 
b) index.html

This HTML file is the entry point of the application, in which we will include all required JS, CSS files to render our components.

<html>
    <head>
        <title>Angular 2 QuickStart</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="css/site.css">
        <link rel="stylesheet" href="css/bootstrap.min.css">
        <!-- 1. Load libraries -->
        <script src="js/core.js"></script>
        <script src="js/zone.js"></script>
        <script src="js/reflect.js"></script>
        <script src="js/system.js"></script>
        <!-- 2. Configure SystemJS -->
        <script src="systemjs.config.js"></script>
        <script>
          System.import('app').catch(function(err){ console.error(err); });
        </script>
    </head>
    <!-- 3. Display the application -->
    <body>

        <div class="container">    
          <myhw>Loading ...</myhw>
        <div>
    </body>
</html>

Finally, you should configure webpack to:

  • Export required node_modules to wwwroot/js folder
  • Transpile main.ts to JavaScript file named main.js

To do that, navigate to the root folder (dotnetcoreapp/), and create webpack.config.js:

module.exports = [
 {
   entry: {
     core: './node_modules/core-js/client/shim.min.js',
     zone: './node_modules/zone.js/dist/zone.js',
     reflect: './node_modules/reflect-metadata/Reflect.js',
     system: './node_modules/systemjs/dist/system.src.js'
   },
   output: {
     filename: './wwwroot/js/[name].js'
   },
   target: 'web',
   node: {
     fs: "empty"
   }
 },
 {
   entry: {
     app: './wwwroot/app/main.ts'
   },
   output: {
     filename: './wwwroot/app/main.js'
   },
   devtool: 'source-map',
   resolve: {
     extensions: ['', '.webpack.js', '.web.js', '.ts', '.js']
   },
   module: {
     loaders: [
       { test: /\.ts$/, loader: 'ts-loader' }
     ]
   }
 }];

F) Running the Web Application

To run the demo, you should write the following command-line using CMD, but first be sure that you are in the root directory of your application:

  • webpack: to transpile TS files to JavaScript files
  • dotnet run: to compile the project and run the server

If the compilation is successful, open the given url on your browser to see the running web application:

References

Points of Interest

I hope that you appreciated this post. Try to download the source code and do not hesitate to leave your questions and comments.

History

  • v1 30/10/2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

O.Nasri
Engineer
France France
Microsoft certified professional in C# ,HTML 5 & CSS3 and JavaScript, Asp.net, and Microsoft Azure.

Comments and Discussions

 
GeneralNice Explanation my vote 5 out 5 Pin
Member 1327101028-Nov-17 0:00
memberMember 1327101028-Nov-17 0:00 
GeneralRe: Nice Explanation my vote 5 out 5 Pin
O.Nasri12-Dec-17 19:58
professionalO.Nasri12-Dec-17 19:58 
Question[My vote of 1] doesnt even work Pin
Member 134525048-Oct-17 13:27
memberMember 134525048-Oct-17 13:27 
AnswerRe: [My vote of 1] doesnt even work Pin
O.Nasri6-Nov-17 8:06
professionalO.Nasri6-Nov-17 8:06 
Questionhow can i reach you? Pin
bhalaniabhishek19-Jul-17 20:24
memberbhalaniabhishek19-Jul-17 20:24 
GeneralMy vote of 5 Pin
bhalaniabhishek19-Jul-17 20:19
memberbhalaniabhishek19-Jul-17 20:19 
GeneralRe: My vote of 5 Pin
O.Nasri6-Nov-17 8:12
professionalO.Nasri6-Nov-17 8:12 
Questionnot able to run Pin
Member 1319514222-May-17 18:36
memberMember 1319514222-May-17 18:36 
I downloaded the zip code and made needed changes on startup.cs by giving local connection. The npm version I have is 3.10.10.

I'm totally new to it and need some help. Please can you help me.

when I give command I get following error


D:\New folder (3)>dotnet new -t web
Invalid input switch:
-t
Template Instantiation Commands for .NET Core CLI.

Usage: dotnet new [arguments] [options]

Arguments:
template The template to instantiate.

Options:
-l|--list List templates containing the specified name.
-lang|--language Specifies the language of the template to create
-n|--name The name for the output being created. If no name is specified, the name of the current directory is used.
-o|--output Location to place the generated output.
-h|--help Displays help for this command.
-all|--show-all Shows all templates
AnswerRe: not able to run solution Pin
Member 1265384323-Jun-17 16:26
memberMember 1265384323-Jun-17 16:26 
Questionrouting Pin
Member 1313522518-Apr-17 2:43
memberMember 1313522518-Apr-17 2:43 
GeneralGood Post Pin
Sendilkumar.M14-Mar-17 19:53
memberSendilkumar.M14-Mar-17 19:53 
GeneralRe: Good Post Pin
O.Nasri6-Nov-17 8:13
professionalO.Nasri6-Nov-17 8:13 
Questionproject.json is not created Pin
casiotexas13-Mar-17 5:18
membercasiotexas13-Mar-17 5:18 
Questionerror Pin
GDdovgani20-Feb-17 11:00
memberGDdovgani20-Feb-17 11:00 
QuestionCannot find name 'System'. Pin
PhaedrusH20-Feb-17 6:52
memberPhaedrusH20-Feb-17 6:52 
Questionwebpack command failed Pin
Larry @Datasmith13-Feb-17 15:11
memberLarry @Datasmith13-Feb-17 15:11 
QuestionCan i use visual studio 2015 update 3 ? Pin
daniweb332-Feb-17 21:39
memberdaniweb332-Feb-17 21:39 
AnswerRe: Can i use visual studio 2015 update 3 ? Pin
O.Nasri4-Feb-17 2:56
professionalO.Nasri4-Feb-17 2:56 
QuestionError running webpack Pin
kslper1-Feb-17 8:34
memberkslper1-Feb-17 8:34 
Questionimporting product.cs class from Models folder Pin
Dudyalu Sravan Kumar Reddy31-Dec-16 4:13
memberDudyalu Sravan Kumar Reddy31-Dec-16 4:13 
AnswerRe: importing product.cs class from Models folder Pin
O.Nasri6-Jan-17 9:54
professionalO.Nasri6-Jan-17 9:54 
GeneralMy vote of 5 Pin
Nikhilesh Shinde20-Dec-16 17:54
memberNikhilesh Shinde20-Dec-16 17:54 
GeneralRe: My vote of 5 Pin
O.Nasri6-Jan-17 9:44
professionalO.Nasri6-Jan-17 9:44 
GeneralMy vote of 5 Pin
Salome Nunez Marquez3-Nov-16 12:53
memberSalome Nunez Marquez3-Nov-16 12:53 
QuestionGood job Men Pin
Salome Nunez Marquez2-Nov-16 5:54
memberSalome Nunez Marquez2-Nov-16 5:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 30 Oct 2016

Stats

127K views
6K downloads
72 bookmarked