TRUNGTQ

Think Big, Act Small, Fail Fast and Learn Rapidly

NAVIGATION - SEARCH

Compressing Web API Responses Using DotNetZip

Web API is very popular for building RESTful Web Services in .NET and performance always plays a vital role in any application. In case of large data which is going through the network traffic, it will decrease the performance of the application.

Compression is a good technique to reduce the size of response data and increase the speed of communication between a client and remote resource.

Here, we are going to use DotNetZip Library to compress the response size. We will go step by step for compressing the response of Web API using DotNetZip Library.

Open Visual Studio and click on File -> New -> Project, as in the below image.

Choose ASP.NET Web Application and enter the name as "CompressingWebAPIResponse" and click OK.


Select Empty from the templates, check the Web API checkbox list, and click OK.

Go to the Solution Explorer and right click on Controllers -> Add -> Controller for creating the Controller for Web API.

Select the Web API 2 Controller - Empty from the scaffold and click "Add" .

Enter the Controller name as EmployeeController.

Similarly, add one Model class as Employee.cs inside Models folder and write some property in this class.

 
  1. public class Employee  
  2.     {  
  3.         public string Id { get; set; }  
  4.         public string Name { get; set; }  
  5.         public string Address{ get; set; }  
  6. }   

Now, add one GET Action method in the EmployeeController for getting all the records as below.

 
  1. public IHttpActionResult GetEmployeeData()  
  2.         {  
  3.             List<Employee> employee = new List<Employee>  
  4.             {  
  5.                 new Employee { Id = "1", Name = "Vivek", Address = "Hyderabad" },  
  6.                 new Employee { Id = "2", Name = "Ranjeet", Address = "Hyderabad" },  
  7.                 new Employee { Id = "3", Name = "Ganesh", Address = "Hyderabad" },  
  8.                 new Employee { Id = "4", Name = "Gajanan", Address = "Hyderabad" },  
  9.                 new Employee { Id = "5", Name = "Vijay", Address = "Hyderabad" },  
  10.                 new Employee { Id = "6", Name = "Ashish", Address = "Hyderabad" },  
  11.                 new Employee { Id = "7", Name = "Praveen", Address = "Hyderabad" },  
  12.                 new Employee { Id = "8", Name = "Santosh", Address = "Hyderabad" }  
  13.             };  
  14.   
  15.             return Ok(employee);  
  16.         }  

Now, using Postman, call this Get method and see the expected data as below.

Click on the Header tab and see the Content-Length in below image.

Now, we are going to see content size after additionof DotnetZip Library and using compression technique.

First of all, we have to install DotnetZip Library through "Manage NuGet packages".

Now, add one new folder as Filters and add one class inside this folder as GzipCompression.cs.

Write the below code in this page.

 
  1. using System.IO;  
  2. using System.Net.Http;  
  3. using System.Web.Http.Filters;  
  4.   
  5. namespace CompressingWebAPIResponse.Filters  
  6. {  
  7.     public class GzipCompressionAttribute : ActionFilterAttribute  
  8.     {  
  9.         public override void OnActionExecuted(HttpActionExecutedContext actionContext)  
  10.         {  
  11.             var content = actionContext.Response.Content;  
  12.             var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;  
  13.             var zlibbedContent = bytes == null ? new byte[0] :  
  14.             CompressionHelper.GzipByte(bytes);  
  15.             actionContext.Response.Content = new ByteArrayContent(zlibbedContent);  
  16.             actionContext.Response.Content.Headers.Remove("Content-Type");  
  17.             actionContext.Response.Content.Headers.Add("Content-encoding""gzip");  
  18.             actionContext.Response.Content.Headers.Add("Content-Type""application/json");  
  19.             base.OnActionExecuted(actionContext);  
  20.         }  
  21.     }  
  22.   
  23.     public class CompressionHelper  
  24.     {  
  25.         public static byte[] GzipByte(byte[] str)  
  26.         {  
  27.             if (str == null)  
  28.             {  
  29.                 return null;  
  30.             }  
  31.   
  32.             using (var output = new MemoryStream())  
  33.             {  
  34.                 using (var compressor = new Ionic.Zlib.GZipStream(output, Ionic.Zlib.CompressionMode.Compress, Ionic.Zlib.CompressionLevel.BestSpeed))  
  35.                 {  
  36.                     compressor.Write(str, 0, str.Length);  
  37.                 }  
  38.                 return output.ToArray();  
  39.             }  
  40.         }  
  41.     }  
  42. }  

In the code given above, we are inheriting the ActionFilterAttribute class for overriding OnActionExecuted Method and we also need a helper class to perform compression.

Now, just put this ActionFilter attribute above the GetEmployeeData method, as shown below.

 
  1. [GzipCompression]  
  2.         public IHttpActionResult GetEmployeeData()  
  3.         {  
  4.             List<Employee> employee = new List<Employee>  
  5.             {  
  6.                 new Employee { Id = "1", Name = "Vivek", Address = "Hyderabad" },  
  7.                 new Employee { Id = "2", Name = "Ranjeet", Address = "Hyderabad" },  
  8.                 new Employee { Id = "3", Name = "Ganesh", Address = "Hyderabad" },  
  9.                 new Employee { Id = "4", Name = "Gajanan", Address = "Hyderabad" },  
  10.                 new Employee { Id = "5", Name = "Vijay", Address = "Hyderabad" },  
  11.                 new Employee { Id = "6", Name = "Ashish", Address = "Hyderabad" },  
  12.                 new Employee { Id = "7", Name = "Praveen", Address = "Hyderabad" },  
  13.                 new Employee { Id = "8", Name = "Santosh", Address = "Hyderabad" }  
  14.             };  
  15.   
  16.             return Ok(employee);  
  17.         }  

Below is the image of the same request with compressed data using gzip compression.

Here, we can clearly see the difference of Content-Length which is changed from 395 to 144. We can also use this code for Controller level compression as per our requirement.

In this easy way, we can compress large response data and increase the application performance.

LINK: http://www.c-sharpcorner.com/article/compressing-web-api-responses-using-dotnetzip/

CRUD USING .NET CORE 1.0, ANGULARJS2, WEBAPI

In our previous article we have seen how to startup with .Net Core. In this article we will take a look on database operation according to previous sample application based previous concept.

If you are new to .Net Core, Please read previous post about .Net Core Startup

In this article we are going to explore,

  1. Create Database
  2. Use Entity Framework Core (Db First Approach),
    1. Overview EF Core
    2. Install Entity Framework
    3. Create Models
    4. Configure EF Service
  3. Use MVC 6
    1. Overview MVC6
    2. Use WebAPI
  4. Use AngularJS2
    1. Component,
    2. Route
    3. Service
  5. Configure Server
    1. Run App inside/outside IIS

Let’s get started with step by step:

Create Database Before we get started with IDE lets create a new database using SSMS2014. Name it as PhoneBook.

Create a table Named Contacts, copy & run the below script in SSMS2014

USE [PhoneBook]
GO

/****** Object:  Table [dbo].[Contacts]    Script Date: 8/7/2016 11:28:55 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Contacts](
	[ContactID] [int] IDENTITY(1,1) NOT NULL,
	[FirstName] [nvarchar](50) NULL,
	[LastName] [nvarchar](50) NULL,
	[Phone] [nvarchar](50) NULL,
	[Email] [nvarchar](50) NULL,
 CONSTRAINT [PK_Contacts] PRIMARY KEY CLUSTERED 
(
	[ContactID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
 

Let’s get started, following our previous topic I am going to use previous sample application, open it with Visual Studio 2015.

core_crud_1

It will automatically started restoring the dependencies. Build & run it. The application is working perfectly.

Install Entity Framework:Before install let’s have an overview on EF Core new Features:

  • Modelling: This includes Basic modelling, Data annotations, Relationships and much more.
  • Change Tracking:This includes Accessing tracked state, Snapshot, Notification change tracking.
  • SaveChanges: This includes Basic save functionality,Async SaveChanges,Transactions.
  • Query: This includes Basic LINQ support, Async query, Raw SQL queries
  • Database schema management: This includes Database creation/deletion APIs, Relational database migrations, and Reverse engineer from database.
  • Database providers: This includes EntityFramework.SqlServer, Sqlite, InMemory
  • Platforms: Supports Universal Windows Platform (UWP), .NET Core, Full .NET

Get more details about EF Core. Let’s add folders for Entity models in our sample app solution. core_crud_3

DbEntities: for model entities. The installation of EF is pretty much simple. Open project.json file, point tools section modify the section with below line.

"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
"Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0"

core_crud_4

Save changes after modification.

core_crud_5

Packages will automatically restored. Let’s get explanation what are those.

EntityFrameworkCore.SqlServer: Database Provider, that allows Entity Framework Core to be used with Microsoft SQL Server.

EntityFrameworkCore.Tools: Command line tool for EF Core. Includes Commands

For Package Manager Console:

  • Scaffold-DbContext,
  • Add-Migration,
  • Udate-Database

For Command Window:

  • dotnet ef dbcontext scaffold

We will see how to use both command. EntityFrameworkCore.SqlServer.Design: Design-time, that allows Entity Framework Core functionality (EF Core Migration) to be used with Microsoft SQL Server. To access the Command line tools we need to add EntityFrameworkCore.Tools intools section of our project.json.

"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"

core_crud_6

Save changes after modification. Command in Package Manager Console:Open package manager console.

core_crud_7

Input below command then hit enter,

Scaffold-DbContext "Server=DESKTOP-4T79RA1;Database=PhoneBook;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models/DbEntities

 core_crud_8core_crud_9

Command in Command Window:Open Command Window navigate to project directory, type

D:\Article\ASP-CORE\CRUD\CoreMVCAngular2\src\CoreMVCAngular>dotnet ef –help

Here a list of option will be shown in command window, we are going to use dbcontext in Commands.

core_crud_10

NextInput below command then hit enter,

D:\Article\ASP-CORE\CRUD\CoreMVCAngular2\src\CoreMVCAngular>dotnet ef dbcontext scaffold "Server=DESKTOP-4T79RA1;Database=PhoneBook;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer --output-dir Models/CwEntities

core_crud_11

Here is a screen shot of both process that execute & generate models. We will keep DbEntities folder to work with & will delete the other folder.

core_crud_12

Configure EF Service: In PhoneBookContext Class add constructor

public PhoneBookContext(DbContextOptions<PhoneBookContext> options) :
       base(options)
{
}
 

In Startup class we need to enable EF services providing the connectionstring to

public void ConfigureServices(IServiceCollection services)
{
  services.AddMvc();
  var connection = @"Server=DESKTOP-4T79RA1;Database=PhoneBook;Trusted_Connection=True;";
  services.AddDbContext<PhoneBookContext>(options => options.UseSqlServer(connection));
}
 

We have configure the EF services in our application, next we will work with MVC6 that is included in ASP.NET Core.

MVC 6:We have already discuss about MVC6 in our previous post, let’s have an overview on MVC6 new Features, once again:

  1. MVC+Web API+Web Pages = MVC6
  2. No System.Web
  3. Web pages & HTTP services is Unified
  4. Dependency injection built in
  5. Dynamic code compilation (Roslyn compiler)
  6. Open source &
  7. Support cross-platform build & run.
  8. Can be hosted in IIS or self-hosted(Outside IIS)

Ok, now let’s add a WebApi Controller to perform CRUD operation to database table. core_crud_13 In Solution Explorer add a new api folder, right click on it > Add New Item > Web API Controller Class > Add. Modify the initial template.

API Controller

[Route("api/[controller]")]
public class ContactController : Controller
{
    private PhoneBookContext _ctx = null;
    public ContactController(PhoneBookContext context)
    {
        _ctx = context;
    }
}
 

core_crud_14

You may notice that there is a new pattern [ ] in MVC6 attribute route, which is [RouteToken]. This mean that the route token is automatically take the controller name.

Like [Route("api/[controller]")] > [Route("api/Contact")]

Another thing is, we know Web API produces XML by default, now in MVC 6 we can set an attribute to change default produces to JSON type by putting attribute in Class label or on method label. In our case we have set it on method label.

[HttpGet("GetContact"), Produces("application/json")]

GET

// GET: api/Contact/GetContact
[HttpGet("GetContact"), Produces("application/json")]
public async Task<object> GetContact()
{
    List<Contacts> contacts = null;
    object result = null;
    try
    {
        using (_ctx)
        {
            contacts = await _ctx.Contacts.ToListAsync();
            result = new
            {
                contacts
            };
        }
    }
    catch (Exception ex)
    {
        ex.ToString();
    }
    return result;
}
 

POST

// POST api/Contact/PostContact
[HttpPost, Route("PostContact")]
public async Task<object> PostContact([FromBody]Contacts model)
{
    object result = null; int message = 0;
    if (model == null)
    {
        return BadRequest();
    }
    using (_ctx)
    {
        using (var _ctxTransaction = _ctx.Database.BeginTransaction())
        {
            try
            {
                _ctx.Contacts.Add(model);
                await _ctx.SaveChangesAsync();
                _ctxTransaction.Commit();
                message = (int)responseMessage.Success;
            }
            catch (Exception e)
            {
                _ctxTransaction.Rollback();
                e.ToString();
                message = (int)responseMessage.Error;
            }

            result = new
            {
                message
            };
        }
    }
    return result;
}
 

PUT

// PUT api/Contact/PutContact/5
[HttpPut, Route("PutContact/{id}")]
public async Task<object> PutContact(int id, [FromBody]Contacts model)
{
    object result = null; int message = 0;
    if (model == null)
    {
        return BadRequest();
    }
    using (_ctx)
    {
        using (var _ctxTransaction = _ctx.Database.BeginTransaction())
        {
            try
            {
                var entityUpdate = _ctx.Contacts.FirstOrDefault(x => x.ContactId == id);
                if (entityUpdate != null)
                {
                    entityUpdate.FirstName = model.FirstName;
                    entityUpdate.LastName = model.LastName;
                    entityUpdate.Phone = model.Phone;
                    entityUpdate.Email = model.Email;

                    await _ctx.SaveChangesAsync();
                }
                _ctxTransaction.Commit();
                message = (int)responseMessage.Success;
            }
            catch (Exception e)
            {
                _ctxTransaction.Rollback(); e.ToString();
                message = (int)responseMessage.Error;
            }

            result = new
            {
                message
            };
        }
    }
    return result;
}
 

DELETE

// DELETE api/Contact/DeleteContactByID/5
[HttpDelete, Route("DeleteContactByID/{id}")]
public async Task<object> DeleteContactByID(int id)
{
    object result = null; int message = 0;
    using (_ctx)
    {
        using (var _ctxTransaction = _ctx.Database.BeginTransaction())
        {
            try
            {
                var idToRemove = _ctx.Contacts.SingleOrDefault(x => x.ContactId == id);
                if (idToRemove != null)
                {
                    _ctx.Contacts.Remove(idToRemove);
                    await _ctx.SaveChangesAsync();
                }
                _ctxTransaction.Commit();
                message = (int)responseMessage.Success;
            }
            catch (Exception e)
            {
                _ctxTransaction.Rollback(); e.ToString();
                message = (int)responseMessage.Error;
            }

            result = new
            {
                message
            };
        }
    }
    return result;
}
 

So our Web API is ready to dealing with data to database, it’s time to work with client side scripting.  

AngularJS2: Our WebAPI is ready to deal with data from server. Now we are going to work in client-side code with typescript (.ts) files. First of all we need to create a master page to present our views in it. core_crud_15

Then we need to point this html file while app start, so let’s go to the startup.cs file to add below code snippet. This is the configuration for the default files Middleware.

Startup.cs

// app-specific root page(Index.html)
DefaultFilesOptions options = new DefaultFilesOptions();
options.DefaultFileNames.Clear();
options.DefaultFileNames.Add("/Index.html");

need to add library

using Microsoft.AspNetCore.Builder;

Now add script library reference to the html page & define view point to load our app component views.

<spa-app>
   <p>
      <img src="img/ajax_small.gif" />  Please wait ...
   </p>
</spa-app>

Then we need to reference our bootstrap file in our page, that import & enable the our angular script to the page.

<script>
   System.config({ packages: { 'app': { defaultExtension: 'js' } }, });
   System.import('app/main').then(null, console.error.bind(console));
</script

Let’s put together all those in Index.html file.

Index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title></title>

    <base href="/">
    <script>document.write('<base href="' + document.location + '" />');</script>
    <script src="../lib-npm/es6-shim/es6-shim.js"></script>
    <script src="../lib-npm/angular2/angular2-polyfills.js"></script>
    <script src="../lib-npm/systemjs/system.src.js"></script>
    <script src="../lib-npm/rxjs/Rx.js"></script>
    <script src="../lib-npm/angular2/angular2.js"></script>
    <script src="../lib-npm/angular2/router.js"></script>
    <script src="../lib-npm/angular2/http.js"></script>

    <link href="../lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
    <div class="container">
        <spa-app>
            <p>
                <img src="img/ajax_small.gif" />  Please wait ...
            </p>
        </spa-app>

    </div>
    <script src="../lib/jquery/dist/jquery.min.js"></script>
    <script src="../lib/bootstrap/dist/js/bootstrap.min.js"></script>
    <script>
        System.config({ packages: { 'app': { defaultExtension: 'js' } }, });
        System.import('app/main').then(null, console.error.bind(console));
    </script>
</body>
</html>
 

  Bootstrap, Model, Component & Route Main.ts

/*This is the spa bootstrap File*/

//---------Import Angular2------------
import {bootstrap}    from 'angular2/platform/browser';
import {enableProdMode, provide} from 'angular2/core';

//---------Import External Components(Main Component)---------
import {MainComponent} from './app.component';

//---------Bootstrap Component---------
enableProdMode();
bootstrap(MainComponent);
App.component.ts

Hide   Shrink    Copy Code
/*Component Default view For SpaRoute */

//---------Import Angular2------------
import {Component, provide} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES, 
        ROUTER_PROVIDERS, LocationStrategy, 
        HashLocationStrategy, APP_BASE_HREF}                  from 'angular2/router';
 
//---------Import External Components---------
import {Home} from './home/home.component';
import {Contact} from './contact/contact.component';

//---------Declare Components---------
@Component({
    selector: 'spa-app',
    directives: [ROUTER_DIRECTIVES], //decorate link 
    templateUrl: 'app/main.view.html',
    providers: [
        ROUTER_PROVIDERS,
        //provide(APP_BASE_HREF, { useValue: '/' })
        provide(LocationStrategy, { useClass: HashLocationStrategy })
    ]
})

//---------Declare Route Config---------
@RouteConfig([
    { path: '/', name: 'Home', component: Home, useAsDefault: true },
    { path: '/Contact/...', name: 'Contact', component: Contact }
])


//---------Export This Component Class---------
export class MainComponent {
    title: string;
    constructor() {
        this.title = 'Welcome to [.NetCore+MVC6+Angular2] SPA';
    }
}


  Home.ts

import {Component} from 'angular2/core';

@Component({
    selector: 'home',
    templateUrl: `app/home/home.view.html`
})
export class Home {
    constructor() {   
    }
}
Contact.model.ts

Hide   Copy Code
export class ContactModel {
    contactId: number;
    firstName: string;
    lastName: string;
    phone: string;
    email: string;
}


Contact.component.ts

//---------Import Angular2------------
import {Component}                                            from 'angular2/core';
import {ROUTER_DIRECTIVES, RouteConfig}                       from 'angular2/router';

//---------Import External Components---------
import {ContactMain}                                          from './contact.main';

//---------Declare Components---------
@Component({
    selector: 'contacts',
    template: `<router-outlet></router-outlet>`,
    directives: [ROUTER_DIRECTIVES]
})
@RouteConfig([
    { path: '/', name: 'ManageContact', component: ContactMain, useAsDefault: true },
])
export class Contact {
    constructor() { }
}
 

  Contact.main.ts

//---------Import Angular2------------
import {Component, OnInit}                                     from 'angular2/core';
import {HTTP_PROVIDERS, Http}                                  from 'angular2/http';
import {ROUTER_DIRECTIVES, RouteConfig}                        from 'angular2/router';
import {FORM_DIRECTIVES,
    FormBuilder, Control, ControlGroup, Validators}            from 'angular2/common';

//---------Import External Components---------
import {ContactModel}                                          from './contact.model';
import {ContactService}                                        from './contact.service';
import {customvalidators}                                      from './customvalidators';

//---------Declare Components---------
@Component({
    selector: 'contact-list',
    templateUrl: `app/contact/contact.view.html`,
    directives: [ROUTER_DIRECTIVES, FORM_DIRECTIVES],
    providers: [ContactService, HTTP_PROVIDERS]
})

//---------Export This Component Class---------
export class ContactMain implements OnInit {

    public resmessage: string;
    public addmessage: string;
    public listmessage: string;
    public contact: ContactModel;
    public contacts: ContactModel[];
    public editContactId: any

    //Form Control
    contactForm: ControlGroup;
    firstName: Control;
    email: Control;
    phone: Control;

    //Constructor
    constructor(private builder: FormBuilder,
        private contactService: ContactService) {
        this.addmessage = 'Add New Contact';
        this.listmessage = 'All Contact';
        this._formGroup();
    }

    ngOnInit() {
        this.resmessage = "";
        this.editContactId = 0;
        this.getContacts();
    }

    //Form Group
    _formGroup() {
        this.firstName = new Control('', Validators.required);
        this.email = new Control('', Validators.compose([Validators.required, customvalidators.emailValidator]));
        this.phone = new Control('');

        this.contactForm = this.builder.group({
            firstName: this.firstName,
            email: this.email,
            phone: this.phone
        });
    }

    //Get All 
    getContacts() {
        //debugger
        this.contactService.getContacts().subscribe(
            contacts => this.contacts = contacts
        );
    }

    //Save Form
    saveContact(contact) {
        //debugger
        this.contactService.saveContact(contact)
            .subscribe(response => {
                this.resmessage = response;
                this.getContacts();
                this.reset();
            });
    }

    //Get by ID
    editContact(e, m) {
        //debugger
        e.preventDefault();
        this.editContactId = m.contactId;
        this.contactService.getContactByID(m.contactId)
            .subscribe(response => {
                this.contact = response;
                this.firstName.updateValue(this.contact.firstName);
                this.email.updateValue(this.contact.email);
                this.phone.updateValue(this.contact.phone);
            });
    }

    //Save Form
    updateContact(contact: any) {
        //debugger
        if (this.editContactId > 0) {
            this.contactService.updateContact(contact, this.editContactId)
                .subscribe(response => {
                    this.resmessage = response;
                    this.getContacts();
                    this.reset();
                });
        }
    }

    //Delete
    deleteContact(e, m) {
        //debugger
        e.preventDefault();
        var IsConf = confirm('You are about to delete ' + m.firstName + '. Are you sure?');
        if (IsConf) {
            this.contactService.deleteContact(m.contactId)
                .subscribe(response => {
                    this.resmessage = response;
                    this.getContacts();
                });
        }
    }

    reset() {
        this.editContactId = 0;
        this._formGroup();
    }
}
 

Let’s take a closer look at below code snippet, we have the service method call in hare, but the unknown term Subscribe -What is it for? Below we have a simple explanation.

this.contactService.getContacts().subscribe(
            contacts => this.contacts = contacts
        );

Subscribe:The subscriber function to be passed to the Observable constructor.  

Services In our service file we have Http service [Get, GetByID, Post, Put, Delete] that connect with WebAPI to perform operation Create, Read, Update & Delete. GET ALL:Performs a request with `get` http method.For Collection of Object

/Get
    getContacts(): Observable<ContactModel[]> {
        //debugger
        return this._http.get(this._getUrl)
            .map(res => <ContactModel[]>res.json())
            .catch(this.handleError);
    }

GET By ID:Performs a request with `get` http method.For Single Object

//GetByID
    getContactByID(id: string): Observable<ContactModel> {
        //debugger
        var getByIdUrl = this._getByIdUrl + '/' + id;
        return this._http.get(getByIdUrl)
            .map(res => <ContactModel>res.json())
            .catch(this.handleError);
    }

POST:Performs a request with `post` http method.

//Post
    saveContact(contact: ContactModel): Observable<string> {
        //debugger
        let body = JSON.stringify(contact);
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        //http.post(url: string, body: string, options ?: RequestOptionsArgs): Observable<Response>
        return this._http.post(this._saveUrl, body, options)
            .map(res => res.json().message)
            .catch(this.handleError);
    }

PUT:Performs a request with `put` http method.

//Put
    updateContact(contact: ContactModel, id: string): Observable<string> {
        //debugger
        var updateUrl = this._updateUrl + '/' + id;
        var body = JSON.stringify(contact);
        var headers = new Headers();
        headers.append('Content-Type', 'application/json');

        //http.post(url: string, body: string, options ?: RequestOptionsArgs): Observable<Response>
        return this._http.put(updateUrl, body, { headers: headers })
            .map(response => response.json().message)
            .catch(this.handleError);
    }

DELETE:Performs a request with `delete` http method.

//Delete
    deleteContact(id: string): Observable<string> {
        //debugger
        var deleteByIdUrl = this._deleteByIdUrl + '/' + id

        //http.post(url: string, options ?: RequestOptionsArgs): Observable<Response>
        return this._http.delete(deleteByIdUrl)
            .map(response => response.json().message)
            .catch(this.handleError);
    }

Observable : [Observable<T>] A representation of any set of values over any amount of time. This the most basic building block of RxJS. Let’s put it together in Contact.service file.

Contact.service.ts

import {Injectable, Component}                            from 'angular2/core';
import {Http, Request, RequestMethod, Response,
    RequestOptions, Headers}                              from 'angular2/http';
import 'rxjs/Rx';
import {Observable}                                       from 'rxjs/Observable';
import {ContactModel}                                     from './contact.model';

@Component({
    providers: [Http]
})

@Injectable()
export class ContactService {
    public headers: Headers;
    constructor(private _http: Http) {
    }

    public _saveUrl: string = '/api/Contact/PostContact/';
    public _updateUrl: string = '/api/Contact/PutContact/';
    public _getUrl: string = '/api/Contact/GetContact/';
    public _getByIdUrl: string = '/api/Contact/GetContactByID/';
    public _deleteByIdUrl: string = '/api/Contact/DeleteContactByID/';

    //Get
    getContacts(): Observable<ContactModel[]> {
        //debugger
        return this._http.get(this._getUrl)
            .map(res => <ContactModel[]>res.json())
            .catch(this.handleError);
    }

    //GetByID
    getContactByID(id: string): Observable<ContactModel> {
        //debugger
        var getByIdUrl = this._getByIdUrl + '/' + id;
        return this._http.get(getByIdUrl)
            .map(res => <ContactModel>res.json())
            .catch(this.handleError);
    }

    //Post
    saveContact(contact: ContactModel): Observable<string> {
        //debugger
        let body = JSON.stringify(contact);
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        //http.post(url: string, body: string, options ?: RequestOptionsArgs): Observable<Response>
        return this._http.post(this._saveUrl, body, options)
            .map(res => res.json().message)
            .catch(this.handleError);
    }

    //Put
    updateContact(contact: ContactModel, id: string): Observable<string> {
        //debugger
        var updateUrl = this._updateUrl + '/' + id;
        var body = JSON.stringify(contact);
        var headers = new Headers();
        headers.append('Content-Type', 'application/json');

        //http.post(url: string, body: string, options ?: RequestOptionsArgs): Observable<Response>
        return this._http.put(updateUrl, body, { headers: headers })
            .map(response => response.json().message)
            .catch(this.handleError);
    }

    //Delete
    deleteContact(id: string): Observable<string> {
        //debugger
        var deleteByIdUrl = this._deleteByIdUrl + '/' + id

        //http.post(url: string, options ?: RequestOptionsArgs): Observable<Response>
        return this._http.delete(deleteByIdUrl)
            .map(response => response.json().message)
            .catch(this.handleError);
    }

    private handleError(error: Response) {
        return Observable.throw(error.json().error || 'Opps!! Server error');
    }
}

Let’s discus about form in angular2, there are two strategy of angular2 form

  1. Template-driven
  2. Model-driven

 Template-driven In template-driven form directive are added declaratively in the template.

<input id="firstName" type="text"
       class="form-control"
       placeholder="FirstName" [ngFormControl]="firstName" required>

Noticed that the validator is added declaratively with the input element “required”.  

Model-driven In our sample app we have used model-driven form that has ngFormModel & ngFormControl. Here ngFormControl is bind with input element to get the input values through the control.

ngFormModel:binding it to a controller variable “contactForm”

<form [ngFormModel]="contactForm">

ngFormControl

<input id="firstName" type="text" 
                       class="form-control" 
                       placeholder="FirstName" [ngFormControl]="firstName">

ControlGroup is contain of several Controls.

//Form Control
contactForm: ControlGroup;
firstName: Control;
email: Control;
phone: Control;

An Injected FormBulder use the builder to create the control group which is pass as key value pairs.

private builder: FormBuilder

//Form Group
_formGroup() {
    //Set Initial Values to the Control & Validators
    this.firstName = new Control('', Validators.required);
    this.email = new Control('', Validators.compose([Validators.required, customvalidators.emailValidator]));
    this.phone = new Control('');

    //Pass the grouped controls as key value pairs
    this.contactForm = this.builder.group({
        firstName: this.firstName,
        email: this.email,
        phone: this.phone
    });
}

The validations are also checked in our component. Below we have our Model-driven complete form. Form

<form [ngFormModel]="contactForm">
            <div class="form-group" [ngClass]="{ 'has-error' : !firstName.valid }">
                <label class="control-label" for="firstName">Username</label>
                <em *ngIf="!firstName.valid">*</em>
                <input id="firstName" type="text" 
                       class="form-control" 
                       placeholder="FirstName" [ngFormControl]="firstName">
            </div>

            <div class="form-group" [ngClass]="{ 'has-error' : !email.valid }">
                <label class="control-label" for="email">Email</label>
                <em *ngIf="!email.valid">*</em>
                <input id="email" type="email" 
                       class="form-control" 
                       placeholder="Email" [ngFormControl]="email">
            </div>

            <div class="form-group">
                <label class="control-label" for="phone">Phone</label>
                <input id="phone" type="text" class="form-control" placeholder="Phone" [ngFormControl]="phone">
            </div>

            <div class="form-group">
                <button type="submit" class="btn btn-danger" (click)="reset()">Reset</button>
                <button type="submit" class="btn btn-primary" (click)="saveContact(contactForm.value)" 
                        *ngIf="editContactId == 0" 
                        [disabled]="!contactForm.valid">Create</button>
                <button type="submit" class="btn btn-success" (click)="updateContact(contactForm.value)" 
                        *ngIf="editContactId > 0" 
                        [disabled]="!contactForm.valid">Update</button>
            </div>
</form>
 

  Here is the complete contact view page which we have used in our application.

Contact.view.html

<div class="row">

    <div class="col-sm-4">
        <h3>Phone Book {{addmessage}}</h3>
        <form [ngFormModel]="contactForm">
            <div class="form-group" [ngClass]="{ 'has-error' : !firstName.valid }">
                <label class="control-label" for="firstName">Username</label>
                <em *ngIf="!firstName.valid">*</em>
                <input id="firstName" type="text" 
                       class="form-control" 
                       placeholder="FirstName" [ngFormControl]="firstName">
            </div>

            <div class="form-group" [ngClass]="{ 'has-error' : !email.valid }">
                <label class="control-label" for="email">Email</label>
                <em *ngIf="!email.valid">*</em>
                <input id="email" type="email" 
                       class="form-control" 
                       placeholder="Email" [ngFormControl]="email">
            </div>

            <div class="form-group">
                <label class="control-label" for="phone">Phone</label>
                <input id="phone" type="text" class="form-control" placeholder="Phone" [ngFormControl]="phone">
            </div>

            <div class="form-group">
                <button type="submit" class="btn btn-danger" (click)="reset()">Reset</button>
                <button type="submit" class="btn btn-primary" (click)="saveContact(contactForm.value)" 
                        *ngIf="editContactId == 0" 
                        [disabled]="!contactForm.valid">Create</button>
                <button type="submit" class="btn btn-success" (click)="updateContact(contactForm.value)" 
                        *ngIf="editContactId > 0" 
                        [disabled]="!contactForm.valid">Update</button>
            </div>
        </form>
        <span class="warning">{{resmessage}}</span>
    </div>
    <div class="col-sm-8">
        <h3>Phone Book {{listmessage}}</h3>
        <table style="width:100%" class="table table-striped">
            <tr>
                <th>ID</th>
                <th>Firstname</th>
                <th>Email</th>
                <th>Phone</th>
                <th>Option</th>
            </tr>
            <tr *ngFor="#contact of contacts">
                <td>{{ contact.contactId }}</td>
                <td>{{ contact.firstName }}</td>
                <td>{{ contact.email }}</td>
                <td>{{ contact.phone }}</td>
                <td>
                    <a href="javascript:void(0)"
                       (click)="deleteContact($event, contact)"
                       class="btn btn-danger btn-xs pull-right">Delete</a>
                    <a href="javascript:void(0)"
                       (click)="editContact($event, contact)"
                       class="btn btn-primary btn-xs pull-right">Edit</a>
                </td>
            </tr>
        </table>
    </div>
</div>
 

  This is all about from our angular section in our sample app, that we have used to perform client-end operation, now it’s time to build & run the application. Next we will get overview on server configuration.  

ConfigureServer: Outside IIS (Weblistener):We can host & run our application without an IIS environment, we need to add command object that tell the hosting to use the HTTP server weblistener (Windows-only).

"commands": {
    "OutsideIIS": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Weblistener --server.urls http://localhost:5001"
  },

Make sure the dependencies is exist

"Microsoft.AspNetCore.Server.WebListener": "0.1.0"

Now go to solution explorer right click project > properties > Debug , change the profile to OutsideIIS. core_crud_16 Set launch URL, then save & run the application. core_crud_17 Server will start with hosting environment details

core_crud_18

The application is running on url http://localhost:5000 with details request info.

core_crud_19

Inside IIS (Kestrel):Kestrel is a cross-platform web server which is run behind IIS or Nginx.

core_crud_20

Change to IIS Express from navigation bar dropdown list to run the application using IIS. Ok let’s run our application to see the way how it’s work.

Output: Here we can see the application output with welcome message & the data is listed from the server through our MVC6 –WebAPI. Here we can perform CRUD operation with required validation.  

core_crud_21

Hope this will help :)

LINK: https://www.codeproject.com/Articles/1118189/CRUD-USING-NET-CORE-ANGULARJS-WEBAPI

 

ASP.NET Core, Angular2 Shopping Cart Using Web API And EF 1.0.1

In this article let’s see how to create a shopping cart using ASP.NET Core, Angular 2, Entity Framework 1.0.1 and Web API with Template pack .

Introduction

In this article let’s see how to create a shopping cart using ASP.NET Core, Angular 2, Entity Framework 1.0.1 and Web API with Template pack .

Note

Kindly read our previous article which  explains in depth about Getting Started with ASP.NET Core Template Pack

In this article let’s see,

  • Creating sample Database and ItemDetails Table in SQL Server to display in our web application.
  • Creating ASP.NET Core Angular 2 Starter Application (.NET Core) using Template pack
  • Creating EF, DBContext Class and Model Class.
  • Creating WEB API
  • Creating our Component TypeScript file to get WEB API JSON result using Http Module.
  • Filtering Items by Item Name. From Item textbox keyup event display the items by search name.
  • Selecting and Adding Items to Shopping Cart.
  • Displaying Total Price, Total Qty and Grand Price Total in Shopping Cart.
  • Displaying Shopping Cart Details.

This article will explain in detail about how to create a Simple Shopping cart using ASP.NET Core, Angular2, Web API and EF with Template Pack. 

In this Shopping Cart Demo application, we have 3 parts

  • Display All Items and Filter Items in HTML Table using Angular2 from WEB API.
  • Display the Selected Items in details before add to Shopping Cart
  • Add the Selected Item to Shopping Cart. Show Price, Quantity and Grand Total of all items in Shopping cart. 

Display All Items and Filter Items

First, we display all item details in the Shopping page using Angular2. All the Item details will be loaded from WEB API. User can also filter the items by Item Name. When users enter any character in the item Name Filter textbox, the related item details will be loaded dynamically from the database to the shopping page. 

Display the Selected Items in details

When user clicks on Image name we display the detailed Item details at the top to add the selected item to shopping cart. When user clicks on the “Add to cart” button the selected item will be added to the Shopping Cart.

Shopping Cart Details

When user clicks on “Add Item to Cart” button, the selected item will be added to the Shopping cart, before adding to cart we check if the Item is already added to the cart. If the item is already added to the cart then we will increment the quantity in Shopping Cart, If the item is not added then newly selected items will be added to Shopping Cart. In the Shopping Cart, we also display the number of Items  that have been added in the shopping cart. In the Shopping cart we also calculate the total Quantity, Total Price and Grand Price of total shopping details which will be displayed at the end of Shopping Item details.

Prerequisites

Make sure you have installed all the prerequisites in your computer. If not, then download and install all, one by one.

  1. First, download and install Visual Studio 2015 with Update 3 from this link.
  2. If you have Visual Studio 2015 and have not yet updated with update 3, download and install the Visual Studio 2015 Update 3 from this link
  3. Download and install .NET Core 1.0.1 
  4. Download and install TypeScript 2.0 
  5. Download and install Node.js v4.0 or above. I have installed V6.9.1 (Download link).
  6. Download and install Download ASP.NET Core Template Pack visz file from this link

Using the code

Step 1 Create a Database and Table

We will create a ItemDetails table to be used for the Shopping Cart Grid data binding.

The following is the script to create a database, table and sample insert query.

Run this script in your SQL Server. I have used SQL Server 2014. 

-- =============================================  
-- Author : Shanu  
-- Create date : 2017-02-03
-- Description : To Create Database,Table and Sample Insert Query  
-- Latest  
-- Modifier : Shanu  
-- Modify date : 2017-02-03  
-- ============================================= 
--Script to create DB,Table and sample Insert data 
USE MASTER 
GO 
-- 1) Check for the Database Exists .If the database is exist then drop and create new DB 
IF EXISTS (SELECT [name] FROM sys.databases WHERE [name] = 'ShoppingDB' ) 
DROP DATABASE ShoppingDB 
GO 
 
CREATE DATABASE ShoppingDB 
GO 
 
USE ShoppingDB 
GO 
 
-- 1) //////////// ItemDetails table 
-- Create Table ItemDetails,This table will be used to store the details like Item Information  
 
IF EXISTS ( SELECT [name] FROM sys.tables WHERE [name] = 'ItemDetails' ) 
DROP TABLE ItemDetails 
GO 
 
CREATE TABLE ItemDetails 
( 
Item_ID int identity(1,1), 
Item_Name VARCHAR(100) NOT NULL, 
Item_Price int NOT NULL, 
Image_Name VARCHAR(100) NOT NULL, 
Description VARCHAR(100) NOT NULL, 
AddedBy VARCHAR(100) NOT NULL, 
CONSTRAINT [PK_ItemDetails] PRIMARY KEY CLUSTERED  
(  
[Item_ID] ASC  
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]  
) ON [PRIMARY]  
 
GO 
 
-- Insert the sample records to the ItemDetails Table 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('Access Point',950,'AccessPoint.png','Access Point for Wifi use','Shanu') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('CD',350,'CD.png','Compact Disk','Afraz') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('Desktop Computer',1400,'DesktopComputer.png','Desktop Computer','Shanu') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('DVD',1390,'DVD.png','Digital Versatile Disc','Raj') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('DVD Player',450,'DVDPlayer.png','DVD Player','Afraz') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('Floppy',1250,'Floppy.png','Floppy','Mak') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('HDD',950,'HDD.png','Hard Disk','Albert') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('MobilePhone',1150,'MobilePhone.png','Mobile Phone','Gowri') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('Mouse',399,'Mouse.png','Mouse','Afraz') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('MP3 Player ',897,'MultimediaPlayer.png','Multi MediaPlayer','Shanu') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('Notebook',750,'Notebook.png','Notebook','Shanu') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('Printer',675,'Printer.png','Printer','Kim') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('RAM',1950,'RAM.png','Random Access Memory','Jack') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('Smart Phone',679,'SmartPhone.png','Smart Phone','Lee') 
Insert into ItemDetails(Item_Name,Item_Price,Image_Name,Description,AddedBy) values('USB',950,'USB.png','USB','Shanu') 
 
select * from ItemDetails
 

Step 2- Create ASP.NET Core Angular 2 application

After installing all the prerequisites listed above and ASP.NET Core Template, click Start >> Programs >> Visual Studio 2015 >> Visual Studio 2015, on your desktop. Click New >> Project. Select Web >> ASP.NET Core Angular 2 Starter. Enter your project name and click OK.

After creating ASP.NET Core Angular 2 application, wait for a few seconds. You will see that all the dependencies are automatically restored.

We will be using all this in our project to create, build and run our Angular 2 with ASP.NET Core Template Pack, WEB API and EF 1.0.1

Step 3 Creating Entity Freamework 

Add Entity Framework Packages

To add our Entity Framework Packages in our ASP.NET Core application. Open the Project.JSON File and in dependencies add the below line.

Note

Here we have used EF version 1.0.1. 

"Microsoft.EntityFrameworkCore.SqlServer": "1.0.1", 
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final" 

When we save the project,.json file we can see the Reference was been Restoring.

After few second we can see Entity framework package has been restored and all reference has been added.

Adding Connection String

To add the connection string with our SQL connection open the “appsettings.json” file .Yes this is a JSON file and this file looks like the below Image by default.

In this appsettings.json file add our connection string  

"ConnectionStrings": { 
    "DefaultConnection": "Server=YOURDBSERVER;Database=StudentsDB;user id=SQLID;password=SQLPWD;Trusted_Connection=True;MultipleActiveResultSets=true;" 
  },


Note : change the SQL connection string as per your local connection.

Next step is we create a folder named “Data” to create our model and DBContext class.

Creating Model Class for Item Details

We can create a model by adding a new class file in our Data Folder. Right Click Data folder and click Add>Click Class. Enter the class name as itemDetails and click Add.

Now in this class we first create property variable, add ItemDetails. We will be using this in our WEB API controller.  

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using System.ComponentModel.DataAnnotations; 
 
namespace Angular2ASPCORE.Data 
{ 
public class ItemDetails 
    { 
        [Key] 
        public int Item_ID { get; set; } 
 
        [Required] 
        [Display(Name = "Item_Name")] 
        public string Item_Name { get; set; } 
 
        [Required] 
        [Display(Name = "Item_Price")] 
        public int Item_Price { get; set; } 
 
        [Required] 
        [Display(Name = "Image_Name")] 
        public string Image_Name { get; set; } 
 
        [Required] 
        [Display(Name = "Description")] 
        public string Description { get; set; } 
 
        [Required] 
        [Display(Name = "AddedBy")] 
        public string AddedBy { get; set; } 
    } 
}


Creating Database Context

DBContext is Entity Framework Class for establishing connection to database.

We can create a DBContext class by adding a new class file in our Data Folder. Right Click Data folder and click Add>Click Class. Enter the class name as ItemContext and click Add.

In this class we inherit DbContext and created Dbset for our ItemDetails table. 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using Microsoft.EntityFrameworkCore; 
 
namespace Angular2ASPCORE.Data 
{ 
    public class ItemContext : DbContext 
    { 
        public ItemContext(DbContextOptions<ItemContext> options) 
            : base(options) { } 
        public ItemContext() { } 
        public DbSet<ItemDetails> ItemDetails { get; set; } 
    } 
}  


Startup.CS

Now we need to add our database connection string and provider as SQL SERVER.To add this we add the below code in Startup.cs file under ConfigureServices method. 

// Add Entity framework . 
            services.AddDbContext<studentContext>(options => 
             options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));


Step 4 Creating Web API

To create our WEB API Controller, right click Controllers folder. Click Add and click New Item.

Click ASP.NET in right side > Click Web API Controller Class. Enter the name as “itemDetailsAPI.cs” and click Add.

In this we are using only Get method to get all the ItemDetails result from database and binding the final result using Angular2 to html file.

Here in this web API we get all ItemDetails and ItemDetails  loaded by condition ItemName. 

[Produces("application/json")] 
    [Route("api/ItemDetailsAPI")]  
    public class ItemDetailsAPI : Controller 
    { 
        private readonly ItemContext _context; 
 
        public ItemDetailsAPI(ItemContext context) 
        { 
            _context = context; 
        } 
 
        // GET: api/values 
 
        [HttpGet] 
        [Route("Details")] 
        public IEnumerable<ItemDetails> GetItemDetails() 
        { 
            return _context.ItemDetails; 
 
        } 
 
 
        // GET api/values/5 
        [HttpGet] 
        [Route("Details/{ItemName}")] 
        public IEnumerable<ItemDetails> GetItemDetails(string ItemName) 
        { 
            //return _context.ItemDetails.Where(i => i.Item_ID == id).ToList(); ; 
            return _cont
      }


To test it we can run our project and copy the get method API path here we can see our API path for get is/api/ItemDetailsAPI/Details

Run the program and paste the above API path to test our output.

To get the Item Details by ItemName, here we can see all the ItemDetails which start from ItemName “DVD” has been loaded.

/api/ItemDetailsAPI/Details/DVD

Working with Angular2

We create all Angular2 related Apps, Modules, Services, Components, and html templates under ClientApp/App folder.

We create “model” folder adding our models and create “shopping” folder under app folder to create our typescript and html file for displaying Item details.

Note - Images Folder

First create a folder called “Images” inside the shopping folder. I have used this folder to display all shopping cart images. If you store shopping image in some other path in your code change accordingly.

Step 5 Creating our First Component TypeScript

Right Click on shopping folder and click on add new Item. Select Client-side from left side and select TypeScript File and name the file as “shopping.component.ts” and click Add.

First we create ItemDetails.ts  and CartItemDetails.ts model as typescript file.

ItemDetails.ts 

//// For ItemDetails
export interface ItemDetails {
    Item_ID: number;
    Item_Name: string;
    Item_Price: number;
    Image_Name: string;
    Description: string;
    AddedBy: string;
}  


CartItemDetails.ts 

export class CartItemDetails {
    constructor(
        public CItem_ID: number,
        public CItem_Name: string,
        public CImage_Name: string,
        public CDescription: string,
        public CAddedBy: string,
        public CItem_Price: number,
        public CQty: number,
        public CTotalPrice: number
    ) { }
}  


We import this class in our Shopping.component for binding the result of JSon results.

In students.component.ts file we have three parts first is the

  1. import part
  2. Next is component part
  3. Next we have the class for writing our business logics.

First we import angular files to be used in our component; here we import http for using http client in our Angular2 component. 

In component we have selector and template. Selector is to give a name for this app and in our html file we use this selector name to display in our html page.

In template we give our output html file name. Here we will create one html file as “students.component.html”.

Export Class is the main class where we do all our business logic and variable declaration to be used in our component template. In this class we get the API method result and bind the result to the student array. 

Here in the code part I have commented each section for easy understanding. 

import { Component, Injectable, Inject, EventEmitter, Input, OnInit, Output, NgModule  } from <a href="mailto:'@angular/core'">'@angular/core'</a>; 
import { FormsModule  } from <a href="mailto:'@angular/forms'">'@angular/forms'</a>; 
import { ActivatedRoute, Router } from <a href="mailto:'@angular/router'">'@angular/router'</a>; 
import { BrowserModule } from <a href="mailto:'@angular/platform-browser'">'@angular/platform-browser'</a>;  
import { Http,Headers, Response, Request, RequestMethod, URLSearchParams, RequestOptions } from "@angular/http"; 
import { ItemDetails } from '../model/ItemDetails'; 
import { CartItemDetails } from '../model/CartItemDetails'; 
 
 
@Component({ 
    selector: 'shopping', 
    template: require('./shopping.component.html') 
}) 
 
 
export class shoppingComponent { 
    //Declare Variables to be used 
 
    //To get the WEb api Item details to be displayed for shopping 
    public ShoppingDetails: ItemDetails[] = [];    
    myName: string; 
 
    //Show the Table row for Items,Cart  and Cart Items. 
    showDetailsTable: Boolean = true; 
    AddItemsTable: Boolean = false; 
    CartDetailsTable: Boolean = false; 
    public cartDetails: CartItemDetails[] = []; 
 
    public ImageUrl = require("./Images/CD.png"); 
    public cartImageUrl = require("./Images/shopping_cart64.png"); 
 
 
    //For display Item details and Cart Detail items 
    public ItemID: number; 
    public ItemName: string = ""; 
    public ItemPrice: number = 0; 
    public Imagename: string = ""; 
    public ImagePath: string = ""; 
    public Descrip: string =  "";    
    public txtAddedBy: string = ""; 
    public Qty: number = 0;  
 
    //For calculate Total Price,Qty and Grand Total price 
    public totalPrice: number = 0; 
    public totalQty: number = 0; 
    public GrandtotalPrice: number = 0; 
 
    public totalItem: number = 0; 
 
 
    //Inital Load 
    constructor(public http: Http) { 
        this.myName = "Shanu"; 
        this.showDetailsTable = true;  
        this.AddItemsTable = false; 
        this.CartDetailsTable = false; 
        this.getShoppingDetails(''); 
    } 
 
    //Get all the Item Details and Item Details by Item name 
    getShoppingDetails(newItemName) { 
      
        if (newItemName == "") { 
            this.http.get('/api/ItemDetailsAPI/Details').subscribe(result => { 
                this.ShoppingDetails = result.json(); 
            }); 
        } 
        else { 
            this.http.get('/api/ItemDetailsAPI/Details/' + newItemName).subscribe(result => { 
                this.ShoppingDetails = result.json(); 
            }); 
        } 
    } 
 
    //Get Image Name to bind 
    getImagename(newImage) {  
        this.ImageUrl = require("./Images/" + newImage); 
    } 
 
    // Show the Selected Item to Cart for add to my cart Items. 
    showToCart(Id, Name, Price, IMGNM, Desc,user) 
    { 
        this.showDetailsTable = true; 
        this.AddItemsTable = true; 
        this.CartDetailsTable = false; 
        this.ItemID = Id; 
        this.ItemName = Name; 
        this.ItemPrice = Price; 
        this.Imagename = require("./Images/" + IMGNM); 
        this.ImagePath = IMGNM 
        this.Descrip = Desc; 
        this.txtAddedBy = user; 
    } 
 
    // to Show Items to be added in cart 
    showCart() { 
        this.showDetailsTable = false; 
        this.AddItemsTable = true; 
        this.CartDetailsTable = true; 
        this.addItemstoCart();  
    } 
    // to show all item details 
    showItems() { 
        this.showDetailsTable = true; 
        this.AddItemsTable = false; 
        this.CartDetailsTable = false;       
    } 
 
    //to Show our Shopping Items details 
 
    showShoppingItems() { 
        if (this.cartDetails.length <= 0) 
        { 
            alert("Ther is no Items In your Cart.Add Items to view your Cart Details !") 
            return; 
        } 
        this.showDetailsTable = false; 
        this.AddItemsTable = false; 
        this.CartDetailsTable = true; 
    } 
 
    //Check the Item already exists in Cart,If the Item is exist then add only the quantity else add selected item to cart. 
    addItemstoCart() { 
       
        var count: number = 0; 
        var ItemCountExist: number = 0; 
        this.totalItem = this.cartDetails.length; 
      if (this.cartDetails.length > 0) { 
          for (count = 0; count < this.cartDetails.length; count++) { 
              if (this.cartDetails[count].CItem_Name == this.ItemName) { 
                  ItemCountExist = this.cartDetails[count].CQty + 1; 
                  this.cartDetails[count].CQty = ItemCountExist; 
              } 
          } 
      } 
            if (ItemCountExist <= 0) 
            {  
                this.cartDetails.push( 
                    new CartItemDetails(this.ItemID, this.ItemName, this.ImagePath, this.Descrip, this.txtAddedBy, this.ItemPrice, 1, this.ItemPrice));  
     
            } 
            this.getItemTotalresult(); 
    } 
 
    //to calculate and display the total price information in Shopping cart. 
     getItemTotalresult() { 
    this.totalPrice = 0; 
    this.totalQty = 0; 
    this.GrandtotalPrice = 0; 
    var count: number = 0; 
    this.totalItem = this.cartDetails.length; 
    for (count = 0; count < this.cartDetails.length; count++) { 
        this.totalPrice += this.cartDetails[count].CItem_Price; 
        this.totalQty += (this.cartDetails[count].CQty); 
        this.GrandtotalPrice += this.cartDetails[count].CItem_Price * this.cartDetails[count].CQty; 
    }   
 
} 
 
    //remove the selected item from the cart. 
    removeFromCart(removeIndex) { 
        alert(removeIndex); 
        this.cartDetails.splice(removeIndex, 1); 
 
        this.getItemTotalresult(); 
    } 
}


Step 6 Creating our First Component HTML File

Right Click on shopping folder and click on add new Item. Select Client-side from left side and select html File and name the file as “shopping.component.html” and click Add.

Write the below html code to bind the result in our html page to display all the Shopping Items and Shopping Cart details.. 

<h1>{{myName}} ASP.NET Core , Angular2 Shopping Cart using   Web API and EF 1.0.1    </h1> 
<hr style="height: 1px;color: #123455;background-color: #d55500;border: none;color: #d55500;" /> 
  
 
<p *ngIf="!ShoppingDetails"><em>Loading Student Details please Wait ! ...</em></p> 
 <!--<pre>{{ ShoppingDetails | json }}</pre>-->  
 
  
<table id="tblContainer" style='width: 99%;table-layout:fixed;'> 
    <tr *ngIf="AddItemsTable"> 
        <td> 
            <table style="background-color:#FFFFFF; border: dashed 3px #6D7B8D; padding: 5px;width: 99%;table-layout:fixed;" cellpadding="2" 
                   cellspacing="2"> 
                <tr style="height: 30px;  color:#ff0000 ;border: solid 1px #659EC7;"> 
                    <td width="40px"> </td> 
                    <td> 
                        <h2> <strong>Add Items to Cart</strong></h2> 
                    </td> 
 
                </tr> 
                <tr> 
                    <td width="40px"> </td> 
                    <td> 
                        <table> 
                            <tr> 
                               
                                <td> 
 
                                    <img src="{{Imagename}}" width="150" height="150" /> 
 
                                </td> 
                                <td width="30"></td> 
                                <td valign="top"> 
                                    <table style="color:#9F000F;font-size:large" cellpadding="4" cellspacing="6"> 
 
                                        <tr> 
                                            <td> 
                                                <b>Item code </b> 
                                            </td> 
 
                                            <td> 
                                                : {{ItemID}} 
                                            </td> 
 
                                        </tr> 
                                        <tr> 
                                            <td> 
                                                <b>   Item Name</b> 
                                            </td> 
 
                                            <td> 
                                                : {{ItemName}} 
                                            </td> 
 
                                        </tr> 
                                        <tr> 
                                            <td> 
                                                <b> Price  </b> 
                                            </td> 
 
                                            <td> 
                                                : {{ItemPrice}} 
                                            </td> 
 
                                        </tr> 
                                        <tr> 
                                            <td> 
                                                <b> Description </b> 
 
                                            </td> 
                                            <td> 
                                                : {{Descrip}} 
                                            </td> 
 
                                        </tr> 
                                        <tr> 
                                            <td align="center" colspan="2"> 
                                                <table> 
 
                                                    <tr> 
                                                        <td> 
                                                            <button (click)=showCart() style="background-color:#4c792d;color:#FFFFFF;font-size:large;width:200px"> 
                                                                Add to Cart 
                                                            </button>  
                                                             
 
                                                        </td> 
                                                        <td rowspan="2"><img src="{{cartImageUrl}}" /></td> 
                                                    </tr> 
 
                                                </table> 
                                            </td> 
                                        </tr> 
                                    </table> 
                                </td> 
                            </tr> 
                        </table> 
                    </td> 
                </tr> 
            </table> 
        </td> 
    </tr> 
    <tr> 
        <td><hr style="height: 1px;color: #123455;background-color: #d55500;border: none;color: #d55500;" /></td> 
    </tr> 
    <tr *ngIf="CartDetailsTable"> 
        <td> 
             
            <table width="100%"> 
                <tr> 
                    <td> 
                        <table style="background-color:#FFFFFF; border: dashed 3px #6D7B8D; padding: 5px;width: 100%;table-layout:fixed;" cellpadding="2" 
                               cellspacing="2"> 
                            <tr style="height: 30px;  color:#123455 ;border: solid 1px #659EC7;"> 
                                <td width="40px"> </td> 
                                <td width="60%"> 
                                    <h1> My Recent Orders Items <strong style="color:#0094ff"> ({{totalItem}})</strong></h1>  
                                </td> 
                                <td align="right"> 
                                    <button (click)=showItems() style="background-color:#0094ff;color:#FFFFFF;font-size:large;width:300px;height:50px; 
                              border-color:#a2aabe;border-style:dashed;border-width:2px;"> 
                                        Add More Items 
                                    </button> 
                                      
                                </td> 
                            </tr> 
                        </table> 
                        
                    </td> 
                </tr> 
                <tr> 
                    <td> 
                        <table style="background-color:#FFFFFF; border:solid 2px #6D7B8D;padding: 5px;width: 100%;table-layout:fixed;" cellpadding="2" cellspacing="2"> 
                            <tr style="height: 30px; background-color:#336699 ; color:#FFFFFF ;border: solid 1px #659EC7;"> 
                                <td width="30" align="center">No</td> 
                                <td width="80" align="center"> 
                                    <b>Image</b> 
                                </td> 
                                <td width="90" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Item Code</b> 
                                </td> 
                                <td width="140" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Item Name</b> 
                                </td> 
                                <td width="160" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Decription</b> 
                                </td> 
                                <td width="90" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Price</b> 
                                </td> 
                                <td width="90" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Quantity</b> 
                                </td> 
                                <td width="90" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Total Price</b> 
                                </td> 
                                <td></td> 
                            </tr> 
 
                            <tbody *ngFor="let detail of cartDetails ; let i = index"> 
 
                                <tr> 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;" align="center"> 
                                        {{i+1}} 
 
                                    </td> 
                                    <td align="center" style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F" *ngIf!="getImagename(detail.CImage_Name)"> 
                                            <img src="{{ImageUrl}}" style="height:56px;width:56px"> 
                                        </span> 
                                    </td> 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.CItem_ID}} 
                                        </span> 
                                    </td> 
 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.CItem_Name}} 
                                        </span> 
                                    </td> 
 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.CDescription}} 
                                        </span> 
                                    </td> 
 
                                    <td align="right" style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.CItem_Price  | number}} 
                                        </span> 
                                    </td> 
 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;" align="right"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.CQty}} 
                                        </span> 
                                    </td> 
 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;" align="right"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.CTotalPrice*detail.CQty  | number}} 
                                        </span> 
                                    </td> 
 
                                    <td align="center" style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                                  <button (click)=removeFromCart(i) style="background-color:#e11919;color:#FFFFFF;font-size:large;width:220px;height:40px;"> 
                                            Remove Item from Cart 
                                        </button> 
                                    </td> 
                                </tr> 
                            </tbody> 
                            <tr> 
                                <td colspan="5" height="40" align="right" > <strong>Total </strong></td> 
                                <td align="right" height="40"><strong>Price: {{ totalPrice | number}}</strong></td> 
                                <td align="right" height="40"><strong>Qty : {{ totalQty | number}}</strong></td> 
                                <td align="right" height="40"><strong>Sum: {{ GrandtotalPrice | number}}</strong></td> 
                                <td></td> 
                            </tr> 
                        </table> 
                    </td> 
                </tr> 
                 
            </table> 
        </td> 
    </tr> 
   
    <tr *ngIf="showDetailsTable"> 
 
        <td> 
            <table width="100%" style="background-color:#FFFFFF; border: dashed 3px #6D7B8D; padding: 5px;width: 100%;table-layout:fixed;" cellpadding="2" 
                               cellspacing="2"> 
                <tr> 
                    <td> 
                        <table style="background-color:#FFFFFF; border: dashed 3px #6D7B8D; padding: 5px;width: 100%;table-layout:fixed;" cellpadding="2" 
                               cellspacing="2"> 
                            <tr style="height: 30px;  color:#134018 ;border: solid 1px #659EC7;"> 
                                <td width="40px"> </td> 
                                <td width="60%"> 
                                    <h2> <strong>Item Details</strong></h2> 
                                </td> 
                                <td align="right"> 
                                    <button (click)=showShoppingItems() style="background-color:#d55500;color:#FFFFFF;font-size:large;width:300px;height:50px; 
                              border-color:#a2aabe;border-style:dashed;border-width:2px;"> 
                                        Show My Cart Items 
                                    </button> 
                                      
                                </td> 
                            </tr> 
                        </table> 
                    </td> 
                </tr> 
                
                <tr> 
                    <td> 
 
                        <table style="background-color:#FFFFFF; border: solid 2px #6D7B8D; padding: 5px;width: 100%;table-layout:fixed;" cellpadding="2" cellspacing="2" *ngIf="ShoppingDetails"> 
 
 
                            <tr style="height: 30px; background-color:#336699 ; color:#FFFFFF ;border: solid 1px #659EC7;"> 
                                <td width="40" align="center"> 
                                    <b>Image</b> 
                                </td> 
 
                                <td width="40" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Item Code</b> 
                                </td> 
 
                                <td width="120" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Item Name</b> 
                                </td> 
 
                                <td width="120" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Decription</b> 
                                </td> 
 
                                <td width="40" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>Price</b> 
                                </td> 
 
                                <td width="90" align="center" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;cursor: pointer;"> 
                                    <b>User Name</b> 
                                </td> 
 
                            </tr> 
                            <tr style="height: 30px; background-color:#336699 ; color:#FFFFFF ;border: solid 1px #659EC7;"> 
                                <td width="40" align="center"> 
                                    Filter By -> 
                                </td> 
 
                                <td width="200" colspan="5" style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;"> 
 
                                    Item Name : 
 
 
                                    <input type="text" (ngModel)="ItemName" (keyup)="getShoppingDetails(myInput.value)" #myInput style="background-color:#fefcfc;color:#334668;font-size:large; 
                                                   border-color:#a2aabe;border-style:dashed;border-width:2px;" /> 
                                </td> 
 
                            </tr> 
 
                            <tbody *ngFor="let detail of ShoppingDetails"> 
                                <tr> 
                                    <td align="center" style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F" *ngIf!="getImagename(detail.image_Name)"> 
                                            <img src="{{ImageUrl}}" style="height:56px;width:56px" (click)=showToCart(detail.item_ID,detail.item_Name,detail.item_Price,detail.image_Name,detail.description,detail.addedBy)> 
                                        </span> 
 
                                    </td> 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.item_ID}} 
                                        </span> 
                                    </td> 
 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.item_Name}} 
                                        </span> 
                                    </td> 
 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.description}} 
                                        </span> 
                                    </td> 
 
                                    <td align="right" style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.item_Price}} 
                                        </span> 
                                    </td> 
 
 
                                    <td style="border: solid 1px #659EC7; padding: 5px;table-layout:fixed;"> 
                                        <span style="color:#9F000F"> 
                                            {{detail.addedBy}} 
                                        </span> 
                                    </td> 
                                </tr> 
                        </table> 
                    </td> 
                    </tr> 
                </table> 
              </td>    
             </tr> 
           </table> 


Build and run the application

Points of Interest

First create the Database and Table in your SQL Server. You can run the SQL Script from this article to create ShoppingDB database and ItemDetails Table and also don’t forget to change the connection string from “appsettings.json”.

LINK: https://www.codeproject.com/Articles/1168725/ASP-NET-Core-Angular-Shopping-Cart-Using-Web-API

Using AngularJs, ASP.NET MVC, Web API and EntityFramework to build NLayered Single Page Web Applications

Building an NLayered, localized, well-structured Single-Page Web Application using AngularJs, ASP.NET MVC, Web API, EntityFramework and ASP.NET Boilerplate.

Simple Task System Screenshot
A screenshot of the sample application.

Contents

  • Introduction
  • Create the application from boilerplate template
  • Create entities
  • Create DbContext
  • Create Database Migrations
  • Define repositories
  • Implement repositories
  • Build application services
  • Build Web API services
  • Develop the SPA
  • Localization
  • Unit testing
  • Summary
  • Article history
  • References

Introduction

In this article, I'll show you how to develop a Single-Page Web Application (SPA) from ground to up using the following tools:

  • ASP.NET MVC and ASP.NET Web API as web framework.
  • Angularjs as SPA framework.
  • EntityFramework as ORM (Object-Relational Mapping) framework
  • Castle Windsor as Dependency Injection framework.
  • Twitter Bootstrap as HTML/CSS framework.
  • Log4Net for logging, AutoMapper for object-to-object mapping.
  • And ASP.NET Boilerplate as startup template and application framework.

ASP.NET Boilerplate [1] is an open source application framework that combines all of these frameworks and libraries to make you start easily to develop your application. It provides us an infrastructure to develop applications in best practices. It naturally supports Dependency Injection, Domain Driven Designand Layered Architecture. The sample application also implements validation, exception handling, localization andresponsive design.

Create the application from boilerplate template

ASP.NET Boilerplate saves our time while starting a new application by providing templates that combinesandconfiguresbest tools to build enterprise level web applications.

Let's go to aspnetboilerplate.com/Templates to build our application from template...

Create template by ASP.NET Boilerplate

Here, I selected SPA (Single Page Application) with AngularJs and EntityFramework. Also entered SimpleTaskSystem for my project name. It created and downloaded my solution.

There are 5 projects included in the solution. Core project for domain (business) layer, Application project for application layer, WebApi project to implement Web Api controllers, Web project for presentation layer and finally EntityFramework project for EntityFramework implementation.

Note: If you download sample solution for this acticle, you will see 7 projects in the solution. I improved template to support NHibernate and Durandal also for same application. If you don't interest in NHibernate or Durandal, just ignore these 2 projects.

Create entities

I'm creating a simple application to create tasks and assing these tasks to people. So, I need Task and Person entities.

Task entity simply defines a Description, CreationTime and a State for a Task. It also has an optional reference to a Person (AssignedPerson):

public class Task : Entity<long>
{
    [ForeignKey("AssignedPersonId")]
    public virtual Person AssignedPerson { get; set; }

    public virtual int? AssignedPersonId { get; set; }

    public virtual string Description { get; set; }

    public virtual DateTime CreationTime { get; set; }

    public virtual TaskState State { get; set; }

    public Task()
    {
        CreationTime = DateTime.Now;
        State = TaskState.Active;
    }
}


Person entity is simpler and just defines Name of the person:

public class Person : Entity
{
    public virtual string Name { get; set; }
}


ASP.NET Boilerplate provides Entity class that defines Id poperty. I derived entities from this Entity class. Task class has an Id of type long since I derived from Entity<long>. Person class has an Id of type int. Since int is the default primary key type, I did not specified it.

I defined entities in the Core project since Entities are parts of domain/business layer.

Create DbContext

As you know, EntityFramework works with DbContext class. We should first define it. ASP.NET Boilerplate template creates a DbContext template for us. I just added IDbSets for Task and Person. This is my DbContext class:

public class SimpleTaskSystemDbContext : AbpDbContext
{
    public virtual IDbSet<Task> Tasks { get; set; }

    public virtual IDbSet<Person> People { get; set; }

    public SimpleTaskSystemDbContext()
        : base("Default")
    {

    }

    public SimpleTaskSystemDbContext(string nameOrConnectionString)
        : base(nameOrConnectionString)
    {
            
    }
}


It uses Default connection string in web.config. It's defined as shown below:

<add name="Default" connectionString="Server=localhost; Database=SimpleTaskSystem; Trusted_Connection=True;" providerName="System.Data.SqlClient" />

Create Database Migrations

We'll use EntityFramework's Code First Migrations to create and maintain the database schema. ASP.NET Boilerplate template has enabled migrations by default and added a Configurationclass as shown below:

internalinternal sealed class Configuration : DbMigrationsConfiguration<SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext context)
    {
        context.People.AddOrUpdate(
            p => p.Name,
            new Person {Name = "Isaac Asimov"},
            new Person {Name = "Thomas More"},
            new Person {Name = "George Orwell"},
            new Person {Name = "Douglas Adams"}
            );
    }
}


In the Seed method, I added four people for initial data. Now, I'll create the initial migration. I opened Package Manager Console and typed the following command:

Visual studio Package manager console

Add-Migration "InitialCreate" command creates a class named InitialCreate as shown below:

public partial class InitialCreate : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.StsPeople",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Name = c.String(),
                })
            .PrimaryKey(t => t.Id);
            
        CreateTable(
            "dbo.StsTasks",
            c => new
                {
                    Id = c.Long(nullable: false, identity: true),
                    AssignedPersonId = c.Int(),
                    Description = c.String(),
                    CreationTime = c.DateTime(nullable: false),
                    State = c.Byte(nullable: false),
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("dbo.StsPeople", t => t.AssignedPersonId)
            .Index(t => t.AssignedPersonId);            
    }
        
    public override void Down()
    {
        DropForeignKey("dbo.StsTasks", "AssignedPersonId", "dbo.StsPeople");
        DropIndex("dbo.StsTasks", new[] { "AssignedPersonId" });
        DropTable("dbo.StsTasks");
        DropTable("dbo.StsPeople");
    }
}


We did create needed classes to create the database, but not created the database yet. To do it, I'll run the following command:

PM> Update-Database

This command runs migrations, creates the database and populates the initial data for us:

Database created by EntityFramework Migrations

When we change Entitiy classes, we can easily create new migration classes using Add-Migration command and update the database with Update-Database command. To learn more about database migrations, see entity framework's documentation.

Define repositories

In domain driven design, repositories used to implement database-specific codes. ASP.NET Boilerplate creates an automatic repository for each entity using generic IRepository interface. IRepository defines common methods for select, insert, update, delete and a few more:

IRepository interface

We can extend these repository upon our needs. I will extend it to create a Task repository. As I want to seperate interface from implementation, I declare interfaces for repositories first. Here, is the Task repository interface:

public interface ITaskRepository : IRepository<Task, long>
{
    List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state);
}


It extends generic IRepository interface of ASP.NET Boilerplate. So, ITaskRepository inherently defines all these methods as default. It can also add it's own methods as I defined GetAllWithPeople(...).

No need to create a repository for Person since default methods are enough for me. ASP.NET Boilerplate provides a way of injecting generic repositories without creating a repository class. We will see it in TaskAppService class in 'Build application services' section..

I defined repository interfaces in the Core project since they are parts of domain/business layer.

Implement repositories

We should implement the ITaskRepository interface defined above. I'm implementing repositories in EntityFramework project. Thus, domain layer becomes completely independent from EntityFramework.

When we created the project template, ASP.NET Boilerplate defined a generic base class for repositories in our project: SimpleTaskSystemRepositoryBase. It's a good practice to have such a base class since we can later add some common methods for our repositories. You can see definition of this class in the code. I just derive from it for TaskRepository implementation:

public class TaskRepository : SimpleTaskSystemRepositoryBase<Task, long>, ITaskRepository
{
    public List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state)
    {
        //In repository methods, we do not deal with create/dispose DB connections, DbContexes and transactions. ABP handles it.
            
        var query = GetAll(); //GetAll() returns IQueryable<T>, so we can query over it.
        //var query = Context.Tasks.AsQueryable(); //Alternatively, we can directly use EF's DbContext object.
        //var query = Table.AsQueryable(); //Another alternative: We can directly use 'Table' property instead of 'Context.Tasks', they are identical.
            
        //Add some Where conditions...

        if (assignedPersonId.HasValue)
        {
            query = query.Where(task => task.AssignedPerson.Id == assignedPersonId.Value);
        }

        if (state.HasValue)
        {
            query = query.Where(task => task.State == state);
        }

        return query
            .OrderByDescending(task => task.CreationTime)
            .Include(task => task.AssignedPerson) //Include assigned person in a single query
            .ToList();
    }
}


TaskRepository is derived from SimpleTaskSystemRepositoryBase and implements ITaskRepository we defined above.

GetAllWithPeople is our specific method to get tasks where AssignedPerson included (pre-fetched) and optionally filtered by some conditions. We can freely use Context (EF's DBContext) object and database in repositories. ASP.NET Boilerplate manages database connection, transaction, creating and disposing the DbContext for us (See documentation for more information)

Build application services

Application services is used to seperate presentation layer from domain layer by providing façade style methods. I defined application services in the Application assembly in the project. First, I define interface for task application service:

public interface ITaskAppService : IApplicationService
{
    GetTasksOutput GetTasks(GetTasksInput input);
    void UpdateTask(UpdateTaskInput input);
    void CreateTask(CreateTaskInput input);
}


ITaskAppService extends IApplicationService. Thus, ASP.NET Boilerplate automatically provides some features for this class (like dependency injection and validation). Now, let's implement ITaskAppService:

public class TaskAppService : ApplicationService, ITaskAppService
{
    //These members set in constructor using constructor injection.
        
    private readonly ITaskRepository _taskRepository;
    private readonly IRepository<Person> _personRepository;
        
    /// <summary>
    ///In constructor, we can get needed classes/interfaces.
    ///They are sent here by dependency injection system automatically.
    /// </summary>
    public TaskAppService(ITaskRepository taskRepository, IRepository<Person> personRepository)
    {
        _taskRepository = taskRepository;
        _personRepository = personRepository;
    }
        
    public GetTasksOutput GetTasks(GetTasksInput input)
    {
        //Called specific GetAllWithPeople method of task repository.
        var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);

        //Used AutoMapper to automatically convert List<Task> to List<TaskDto>.
        return new GetTasksOutput
                {
                    Tasks = Mapper.Map<List<TaskDto>>(tasks)
                };
    }
        
    public void UpdateTask(UpdateTaskInput input)
    {
        //We can use Logger, it's defined in ApplicationService base class.
        Logger.Info("Updating a task for input: " + input);

        //Retrieving a task entity with given id using standard Get method of repositories.
        var task = _taskRepository.Get(input.TaskId);

        //Updating changed properties of the retrieved task entity.

        if (input.State.HasValue)
        {
            task.State = input.State.Value;
        }

        if (input.AssignedPersonId.HasValue)
        {
            task.AssignedPerson = _personRepository.Load(input.AssignedPersonId.Value);
        }

        //We even do not call Update method of the repository.
        //Because an application service method is a 'unit of work' scope as default.
        //ABP automatically saves all changes when a 'unit of work' scope ends (without any exception).
    }

    public void CreateTask(CreateTaskInput input)
    {
        //We can use Logger, it's defined in ApplicationService class.
        Logger.Info("Creating a task for input: " + input);

        //Creating a new Task entity with given input's properties
        var task = new Task { Description = input.Description };

        if (input.AssignedPersonId.HasValue)
        {
            task.AssignedPersonId = input.AssignedPersonId.Value;
        }

        //Saving entity with standard Insert method of repositories.
        _taskRepository.Insert(task);
    }
}

TaskAppService
uses repositories for database operations. It gets references in it's constructor via constructor injection pattern. ASP.NET Boilerplate naturally implements dependency injection, so we can use constructor injection or property injection freely (See more on dependency injection in ASP.NET Boilerplate documentation).

Notice that we're using PersonRepository by injecting IRepository<Person>. ASP.NET Boilerplate automatically creates repositories for our entities. If default methods of IRepository enough for us, we don't have to create repository classes.

Application service methods works with Data Transfer Objects (DTOs). It's a best practice and I definitely suggest to use this pattern. But you don't have to do it as long as you can deal with problems of exposing Entities to presentation layer.

In the GetTasks method, I used the GetAllWithPeople method that I implemented before. It returns a List<Task> but I need to return a List<TaskDto> to presentation layer. AutoMapper helps us here to automatically convert Task objects to TaskDto objects. GetTasksInput and GetTasksOutput are special DTOs defined for GetTasks method.

In the UpdateTask method, I retrived the Task from database (using IRepository's Get method) and changed peoperties of the Task. Notice that I did not even called Update method of the repository. ASP.NET Boilerplate implements UnitOfWork pattern. So, all changes in an application service method are a unit of work (atomic) and applied to database at the end of the method automatically.

In the CreateTask method, I simply created a new Task and inserted to database using the IRepository's Insert method.

ASP.NET Boilerplate's ApplicationService class has some properties to make developing application services easier. For example, it defines a Logger property for logging. So, we derived TaskAppService from ApplicationService and used it's Logger property here. It's optional to derive from this class but required to implement IApplicationService (notice that ITaskAppService extends IApplicationService).

Validation

ASP.NET Boilerplate automatically validates inputs of application service methods. CreateTask method gets CreateTaskInput as parameter:

public class CreateTaskInput
{
    public int? AssignedPersonId { get; set; }

    [Required]
    public string Description { get; set; }
}


Here, Description is marked as Required. You can use any Data Annotation attributes here. If you want to make some custom validation, you can implement ICustomValidate as I implemented in UpdateTaskInput:

public class UpdateTaskInput : ICustomValidate
{
    [Range(1, long.MaxValue)]
    public long TaskId { get; set; }

    public int? AssignedPersonId { get; set; }

    public TaskState? State { get; set; }

    public void AddValidationErrors(List<ValidationResult> results)
    {
        if (AssignedPersonId == null && State == null)
        {
            results.Add(new ValidationResult("Both of AssignedPersonId and State can not be null in order to update a Task!", new[] { "AssignedPersonId", "State" }));
        }
    }

    public override string ToString()
    {
        return string.Format("[UpdateTask > TaskId = {0}, AssignedPersonId = {1}, State = {2}]", TaskId, AssignedPersonId, State);
    }
}


AddValidationErrors method is the place you can write your custom validation code.

Handling exceptions

Note that we did not handled any exception. ASP.NET Boilerplate automatically handles exceptions, logs and returns an appropriate error message to the client. Also, in client side, handles these error messages and show to the user. Actually, this is true for ASP.NET MVC and Web API Controller actions. Since we will expose the TaskAppService using Web API, we don't need to handle exceptions. See exception handling document for details.

Build Web API services

I want to expose my application services to remote clients. Thus, my AngularJs application can easily call these service methods using AJAX.

ASP.NET Boilerplate provides an automatic way of exposing application service methods as ASP.NET Web API. I just use DynamicApiControllerBuilder as shown below:

DynamicApiControllerBuilder
    .ForAll<IApplicationService>(Assembly.GetAssembly(typeof (SimpleTaskSystemApplicationModule)), "tasksystem")
    .Build();


For this example, ASP.NET Boilerplate finds all interfaces inherits IApplicationService in Application layer assembly and creates a web api controller for each application service class. There are alternative syntaxes for fine control. We'll see how to call these services via AJAX.

Develop the SPA

I'll implement a Single-Page Web Application as user interface of my project. AngularJs (by Google) is one (propably the top one) of the most used SPA frameworks.

ASP.NET Boilerplate provides a template that makes easy to start with AngularJs. The template has two pages (Home and About) with smooth transition between pages. Uses Twitter Bootstrap as HTML/CSS framework (thus, it's responsive). It's also localized into English and Turkish with ASP.NET Boilerplate's localization system (You can easly add other languages or remove one of them).

We first change route of the template. ASP.NET Boilerplate template uses AngularUI-Router, the de-facto standard router of AngularJs. It provides state based routing modal. We will have two views: task list and new task. So, we will change route definition in app.js as shown below:

app.config([
    '$stateProvider', '$urlRouterProvider',
    function ($stateProvider, $urlRouterProvider) {
        $urlRouterProvider.otherwise('/');
        $stateProvider
            .state('tasklist', {
                url: '/',
                templateUrl: '/App/Main/views/task/list.cshtml',
                menu: 'TaskList' //Matches to name of 'TaskList' menu in SimpleTaskSystemNavigationProvider
            })
            .state('newtask', {
                url: '/new',
                templateUrl: '/App/Main/views/task/new.cshtml',
                menu: 'NewTask' //Matches to name of 'NewTask' menu in SimpleTaskSystemNavigationProvider
            });
    }
]);


app.js is the main javascript file to configure and start our SPA. Notice that we're using cshtml files as views! Normally, html files are used as views in AngularJs. ASP.NET Boilerplate makes it possible to use cshtml files. Thus we will have the power of razor engine to generate HTML.

ASP.NET Boilerplate provides an infrastructure to create and show menus in an application. It allows to define menu in C# and use same menu both in C# and javascript. See SimpleTaskSystemNavigationProvider class for creating menu and see header.js/header.cshtml for showing menu in the angular way.

First, I'm creating an Angular controller for the task list view:

(function() {
    var app = angular.module('app');

    var controllerId = 'sts.views.task.list';
    app.controller(controllerId, [
        '$scope', 'abp.services.tasksystem.task',
        function($scope, taskService) {
            var vm = this;

            vm.localize = abp.localization.getSource('SimpleTaskSystem');

            vm.tasks = [];

            $scope.selectedTaskState = 0;

            $scope.$watch('selectedTaskState', function(value) {
                vm.refreshTasks();
            });

            vm.refreshTasks = function() {
                abp.ui.setBusy( //Set whole page busy until getTasks complete
                    null,
                    taskService.getTasks({ //Call application service method directly from javascript
                        state: $scope.selectedTaskState > 0 ? $scope.selectedTaskState : null
                    }).success(function(data) {
                        vm.tasks = data.tasks;
                    })
                );
            };

            vm.changeTaskState = function(task) {
                var newState;
                if (task.state == 1) {
                    newState = 2; //Completed
                } else {
                    newState = 1; //Active
                }

                taskService.updateTask({
                    taskId: task.id,
                    state: newState
                }).success(function() {
                    task.state = newState;
                    abp.notify.info(vm.localize('TaskUpdatedMessage'));
                });
            };

            vm.getTaskCountText = function() {
                return abp.utils.formatString(vm.localize('Xtasks'), vm.tasks.length);
            };
        }
    ]);
})();


I defined name of the controller as 'sts.views.task.list'. This my convention (for scalable code-base) but you can simply name it as 'ListController'. AngularJs also uses dependency injection. We're injecting '$scope' and 'abp.services.tasksystem.task' here. First one is Angular's scope variable, second one is the automatically created javascript service proxy for ITaskAppService (we built it before in 'Build Web API services' section).

ASP.NET Boilerplate provides infrastructure to use same localization texts both in server and client (see it's documentation for details). 

vm.taks is the list of tasks that will be shown in the view. vm.refreshTasks method fills this array by getting tasks using taskService. It's called when selectedTaskState changes (observed using $scope.$watch).

As you see, calling an application service method is very easy and straightforward! This is a feature of ASP.NET Boilerplate. It generates Web API layer and Javascript proxy layer that talks with this Web API layer. Thus, we are calling the application service method as calling a simple javascript method. It is completely integrated with AngularJs (uses Angular's $http service).

Let's see the view side of task list:

<div class="panel panel-default" ng-controller="sts.views.task.list as vm">

    <div class="panel-heading" style="position: relative;">
        <div class="row">
            
            <!-- Title -->
            <h3 class="panel-title col-xs-6">
                @L("TaskList") - <span>{{vm.getTaskCountText()}}</span>
            </h3>
            
            <!-- Task state combobox -->
            <div class="col-xs-6 text-right">
                <select ng-model="selectedTaskState">
                    <option value="0">@L("AllTasks")</option>
                    <option value="1">@L("ActiveTasks")</option>
                    <option value="2">@L("CompletedTasks")</option>
                </select>
            </div>
        </div>
    </div>

    <!-- Task list -->
    <ul class="list-group" ng-repeat="task in vm.tasks">
        <div class="list-group-item">
            <span class="task-state-icon glyphicon" ng-click="vm.changeTaskState(task)" ng-class="{'glyphicon-minus': task.state == 1, 'glyphicon-ok': task.state == 2}"></span>
            <span ng-class="{'task-description-active': task.state == 1, 'task-description-completed': task.state == 2 }">{{task.description}}</span>
            <br />
            <span ng-show="task.assignedPersonId > 0">
                <span class="task-assignedto">{{task.assignedPersonName}}</span>
            </span>
            <span class="task-creationtime">{{task.creationTime}}</span>
        </div>
    </ul>

</div>


ng-controller attribute (in the first line) binds the controller to the view. @L("TaskList") gets localized text for "task list" (works on server while rendering HTML). It's possible since this is a cshtml file.

ng-model binds combobox and the javascript variable. When the variable changes, the combobox updated. When the combobox changes, the valiable is updated. This is two-way binding of AngularJs.

ng-repeat is another 'directive' of Angular that is used to render same HTML for each value in an array. When the array changes (an item is added for example), it's automatically reflected to the view. This is another powerful feature of AngularJs.

Note: When you add a javascript file (for example, for the 'task list' controller), you should add it to your page. This can be done by adding it to Home\Index.cshtml in the template.

Localization

ASP.NET Boilerplate provides a flexible and strong localization system. You can use XML files or Resource files as localization source. You can also define custom localization sources. See documentation for more. In this sample application, I used XML files (it's under Localization folder in web application):

<?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="en">
  <texts>
    <text name="TaskSystem" value="Task System" />
    <text name="TaskList" value="Task List" />
    <text name="NewTask" value="New Task" />
    <text name="Xtasks" value="{0} tasks" />
    <text name="AllTasks" value="All tasks" />
    <text name="ActiveTasks" value="Active tasks" />
    <text name="CompletedTasks" value="Completed tasks" />
    <text name="TaskDescription" value="Task description" />
    <text name="EnterDescriptionHere" value="Task description" />
    <text name="AssignTo" value="Assign to" />
    <text name="SelectPerson" value="Select person" />
    <text name="CreateTheTask" value="Create the task" />
    <text name="TaskUpdatedMessage" value="Task has been successfully updated." />
    <text name="TaskCreatedMessage" value="Task {0} has been created successfully." />
  </texts>
</localizationDictionary>
 

Unit testing

ASP.NET Boilerplate is designed to be testable. I authored an article to show unit and integration testing for ABP based projects. See the article: Unit testing in C# using xUnit, Entity Framework, Effort and ASP.NET Boilerplate.

Summary

In this article, I demonstrated how to develop an NLayered ASP.NET MVC web application with a SPA and responsive user interface. I used ASP.NET Boilerplate since it makes easy to develop such applications using best practices and saves our time. Use these links for moe:

LINK: https://www.codeproject.com/Articles/791740/Using-AngularJs-ASP-NET-MVC-Web-API-and-EntityFram

Single Page Application (SPA) for Enterprise App (Angular2 & WebApi) - Part 1 - Overview

Một tác giả người Việt, bài viết khá chất lượng, có thể tham khảo.
 
How to build the SPA for enterprise application using Angular2 and WebApi (RESTful)

Other Articles in the series

  1. Overview
  2. Add new Permission
  3. Project structure
  4. Multi-Languages (i18n)
  5. DI & IoC - Why and Why not?
  6. RESTful & WebApi
  7. Manage Application Lifecycle
  8. Build & Deploy Application

Introduction

please visit my blog at www.tranthanhtu.vn for reading more articles about coding

Note: In my series of this article, I use free template at https://colorlib.com/polygon/gentelella

The first thing, I would like to share to you is "We did not learn technogies, we learn how to use technologies for our business".

Note: In this code, we use Relase Candidate version of Angular 2

Nowadays, with growing of web development, more and more desktop applications (such as HRM, CRM, Payroll, ...)  were migrated to web application.

So the web application can utilize much benefit from web environment. This is easy to search the pros and cons between web application and desktop application on the internet, so we will not discuss here.

Purpose of series of articles will introduce you how did I organise the project for those types of application.

Some highlight technologies:

  • Angular2 (typescript)
  • WebApi (RESTful)
  • Entity Framework
  • Bootstrap
  • IoC
  • Multi-Layer architecture
  • Modular your Application
  • Multi Languages
  • Design Pattern (Unit Of Work, Repository, …)
  • SOLID principle
  • Gulp
  • NodeJs

There are many mistake and improvements need to be done so far.

How to get the code

Checkout the code at https://github.com/techcoaching/TinyERP.git

How to run the code

The code was organized in 2 folders:

  • Client: this is client code written by typescript using angular2 framework.
  • Api: this is server side code where handles request from client.

Run client side:

  1. To run the client side code, please check and install if missing:
  1. Run the app

- Open command prompt and go to the root folder of client:

- Type “npm install” for installing missing nodejs packages.

- Use “npm start” to run the app:

Output in console:

Output on the browser:

To this point, we can run the client successfully

Run API

- For the first run, We need to update the connectionstring in app.common\configurations\configuration.(debug|release).config

- Open Api project in visual studio (2015).

- Press f5 to run the Api on IIS Express, we will see the output on browser:

Config the default route, will remove this error page.

The First Look At The App

- We need to login before continueing:

- Login with tu.tran@yahoo.com/123456, we will see the default page as below (this can be changed in application configuration):

 

List of Roles Page

In this section, we will go through how we implement new page using my code.

Analyse

As my habit, I analyse the page before implement, this will help us recognise:

  • Listing out The steps we need to do to complete the page.
  • Figure out the missing information, so we can ask for help immediatedly.
  • Think about the flow of logic from the client to server side, from UI to repository. So the written code will be nicer. For this, many of my co-workers, they try to write the code first, and debug it after that. When something wrong, it changes your behavior, and we try to change the code to make it works. this may break the logic flow and the code is not follow the convention, architecture was used in the app. This will raise some new potential issues in the future and the code is hard for manteance.

After analysing the page, we figure out the list of things need to be completed as below:

On client:

+ Create new module (called security), I will discusss more pros and cons on how to modular the application.

+ Register route for Roles component (page in angular was called component) and appropriated menu item information (sub menu on th eleft panel)

+ Import and register this setting module with application config.

+ Create component files (html file for UI, ts file for logic handling and ts for view model of that component).

+ Implemnet the UI of Roles component

+ Implement logic of Roles component (also calling to server and getting data)

+ Implement service for this page (service willmake the call to REST api for getting the list of permission).

On Api:

+ Add new controller called RolesController for handling request related to Role.

+ Add RoleService and RoleRepository for getting the list of Roles (I use multi-layers architecture in this case)

+ Add entity into appropriated DbContext.

Ok, Now we will go through step by step in detail.

Implement client

In this section, <root> folder is the folder of client code (the path to "client" folder).

  1. Create new module (called security). as the convention of the app, we create new module.ts file in "<root>/app/modules/secutiry/_share/config" as photo below:

and route.ts file

In this file, we will provide the setting information for security module, such as: list of sub menu items, routing in module, ...

  1. Register route for Roles component (page in angular was called component) and appropriated menu item information (sub menu on th eleft panel). Add this line into the module.ts file created in above step.

  1. Import and register this setting module with application config. This will register the security module in the application, show the module and its sub menu items will be displayed on the left panel.

The sytem will auto register routes in security module for us automaticly.

  1. Create component files (html file for UI, ts file for logic handling and ts for view model of that component).

  1. Implemnet the UI of Roles component, there are some directives already created, we use in this article. I will explain more detail in other article how to implement those directives in other articles.

In this html, we use:

  • "grid" directive, for displaying the list of roles. there are some events we can passnig the handlers and  columns of grid in mode.options property.
  • "page-action" directive, this will dispaly the "Add Role" on the list of Roles as photo below:

  • page, page-header, page-content directives, this is the structure of the page in my framework.
  1. Implement logic of Roles component (also calling to server and getting data)

For declare new componnet we need to:

  • Specify the template file for the component (line 9).
  • Declare the number of directives were used in component html file (line 11). Is the directive was not declare here, it will not be redered in roles.html file and angular will consider its tag (<page-action/>, <grid />) as html tag
  • Declare new Roles class inherit  from BasePage.
  • Declare the model for the component. each component should have its own view model, this help us reducing the complexity of roles.ts file.
  • Getting data from api by calling appropriated method of roleService. We move this to service and let the roles.ts focus on the behavior of the page.

Note: we pass i18nHelper into constructor of RolesModel. this will be used for resolving the lable of grid columns base on the current languages (multi-language supported).

  1. Implement service for this page (service willmake the call to REST api for getting the list of permission).

This is just a simple call to REST api for getting the list of roles. For more information about IConnection, see <root>/app/common/connectors/RESTConnector.ts

Implement Api

In this section, <root> folder is the folder of api code (the path to "api" folder).

  1. Api project structure:

  • App.Common: this contains the common code can be used everywhere in the app
  • App.Api: this is public REST api that client side can call.
  • App.Service: this contains all service interfaces and its appropriated DTOs.
  • App.Service.Impl: this only contains implementation of appropriated interface in App.Service.
  • App.Repository: this contains all repository interfaces. Each repository only operates on 1 entity in database.
  • App.Repository.Impl: this only contains implementation of appropriated interface in App.Resitory.
  • App.Entity: This contains all entities of the app.
  • App.Context: Currently, this containts the all DbContexts in the app, as we use EF framework. I intend to merge this with the App.Entity in the future.

Note: The communication between the layer (such as: App.Api -> App.Service => App.Repository), we use the interface only.

 

  1. Add new controller called RolesController for handling request related to Role.

As the convention of the app, we create new RolesController in "App.Api/Features/Security"

namespace App.Api.Features.Security
{
    [RoutePrefix("api/roles")]
    public class RolesController : ApiController
    {
        [HttpGet]
        [Route("")]
        public IResponseData<IList<RoleListItemSummary>> GetRoles()
        {
            IResponseData<IList<RoleListItemSummary>> response = new ResponseData<IList<RoleListItemSummary>>();
            try
            {
                IRoleService roleService = IoC.Container.Resolve<IRoleService>();
                IList<RoleListItemSummary> roles=roleService.GetRoles();
                response.SetData(roles);
            }
            catch (ValidationException ex)
            {
                response.SetErrors(ex.Errors);
                response.SetStatus(System.Net.HttpStatusCode.PreconditionFailed);
            }
            return response;
        }
    }
}
 

I think the code is rather simple. From controller we call "GetRoles" of the appropriated service interface and get the list of roles (dto).

IoC will return the concrete instance of that service.

In App.Service.Impl, we have boostrap.cs file, this is the place for registering the service interface and its concrete implementation.

namespace App.Service.Impl
{
    public class Bootstrap : App.Common.Tasks.BaseTask<IBaseContainer>, IBootstrapper
    {
        public Bootstrap():base(App.Common.ApplicationType.All)
        {
        }
        public void Execute(IBaseContainer context)
        {
            context.RegisterSingleton<App.Service.Security.IRoleService, App.Service.Impl.Security.RoleService>();
        }
    }
}
 
  1. Add IRoleService and RoleService for getting the list of Roles (I use multi-layers architecture in this case)
namespace App.Service.Security
{
    public interface IRoleService
    {
        System.Collections.Generic.IList<RoleListItemSummary> GetRoles();
    }
}

namespace App.Service.Impl.Security
{
    internal class RoleService : IRoleService
    {
        public IList<RoleListItemSummary> GetRoles()
        {
            IRoleRepository repository = IoC.Container.Resolve<IRoleRepository>();
            return repository.GetItems<RoleListItemSummary>();
        }
    }
}


Note:

  • We should define appropriate DTOs for each action, as most cases, we just get some properties of entity and this will help the code easier for mantaince in future.
  • The RoleService class was delared as internal, not a public class. So outside of this dll can not create instance of this class

 

  1. Add IRoleRepository and RoleRepository for getting the list of Roles
namespace App.Repository.Secutiry
{
    public interface IRoleRepository: App.Common.Data.IBaseContentRepository<Role>
    {
    }
}

namespace App.Repository.Impl.Security
{
    internal class RoleRepository: BaseContentRepository<Role>, IRoleRepository
    {
        public RoleRepository() : base(new App.Context.AppDbContext(App.Common.IOMode.Read))
        {
        }

        public RoleRepository(IUnitOfWork uow) : base(uow.Context as IMSSQLDbContext)
        {
        }
    }
}


Note: The same as RoleService, RoleRepository class also was delared as internal. So only the interface can be used outside of this project.

  1. Add entity into appropriated DbContext:
namespace App.Entity.Security
{
    public class Role:BaseContent
    {
        public IList<Permission> Permissions { get; set; }
        public Role():base()
        {
            this.Permissions = new List<Permission>();
        }
        public Role(string name, string desc, IList<Permission> permissions): this() {
            this.Name = name;
            this.Key = App.Common.Helpers.UtilHelper.ToKey(name);
            this.Description = desc;
            if (permissions == null) { return; }
            this.Permissions = permissions;
        }
    }
}

namespace App.Context
{
    public class AppDbContext : App.Common.Data.MSSQL.MSSQLDbContext
    {
        public AppDbContext(IOMode mode = IOMode.Read) : base(new App.Common.Data.MSSQL.MSSQLConnectionString(), mode)
        {
        }
        public System.Data.Entity.DbSet<Role> Roles { get; set; }
    }
}
 
For more information about Angular, Please have a look at Angular2 - Overview

Summary

In this article, I provider you an overview about how to get the list of roles and display them on the ui.

In next article, we continue with how to create and update roles, and go  into more detail of framework (such as: project structure, directives, ....).

For more information about: