TRUNGTQ

Think Big, Act Small, Fail Fast and Learn Rapidly

NAVIGATION - SEARCH

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

ASP.NET Core : Overview Of Dependency Injection

Introduction

A software developer writes a lot of code that is tightly coupled; and when complexity grows, the code will eventually deteriorate into spaghetti code; in other words, the application design being a bad design.

Dependency Injection (DI) is a pattern where objects are not responsible for creating their own dependencies. Dependency Injection is a way to remove hard-coded dependencies among objects, making it easier to replace an object's dependencies, either for testing (using mock objects in unit test) or to change run-time behavior.

Before understanding Dependency Injection, we should be familiar with the two concepts of Object Oriented Programming - tight coupling and loose coupling. Let's see each, one by one.

Tight Coupling

When a class is dependent on a concrete dependency, it is said to be tightly coupled to that class. A tightly coupled object is dependent on another object; that means changing one object in a tightly coupled application often requires changes to a number of other objects. It is not difficult when an application is small but in an enterprise level application, it is too difficult to make the changes.

Loose Coupling

It means two objects are independent and an object can use another object without being dependent on it. It is a design goal that seeks to reduce the inter- dependencies among components of a system with the goal of reducing the risk that changes in one component will require changes in any other component.

Now in short, Dependency Injection is a pattern that makes objects loosely coupled instead of tightly coupled. When we are designed classes with DI, they are more loosely coupled because they do not have direct, hard-coded dependencies on their collaborators. This follows the Dependency Inversion Principle(DIP).

There are three types of dependency injections,

  1. Constructor Dependency Injection
  2. Setter Dependency Injection
  3. Interface Dependency Injection

Dependency Inversion Principle (DIP)

It is the fifth principle of SOLID where “D” stands for Dependency Inversion Principle. Its main goal is decoupling software modules, in other words software design should be loosely coupled instead of tightly coupled. The principle states:

  1. High-level modules should not depend upon low-level modules. Both should depend upon abstractions.
  2. Abstractions should not depend upon details. Details should depend upon abstractions.

In short, the higher-level module defines an interface and lower-level module implements that interface. To explain this sentence we use a real-life example.

Suppose you are sitting on your desk. Your desk has some gadgets, like your development machine (LCD Monitor or Laptop) and mobile phone. The LCD Monitor has a cable that connects from the electric port (power cable) and the same as the mobile phone that also has a charging cable that also connects to an electric port. You could see that both devices connect from the electric port so the question occurs of who defined the port, your device or the cable? You will say that the devices define the port, in other words we don't purchase devices depending on port while the port is designed dependent on devices and the cable is just an interface that connects both devices and the port so you could say that a high-level module doesn't depend on the low-level module but both should be dependent on abstraction.


Inversion of Control (IoC) Pattern

In pratice, an application is deigned with many classes. So when we use DI with these classes then these classes are requesting their dependecies via their constructor that’s why it's helpful to have a class dedicated to creating these classes with their associated dependencies. These classes are referred to as containers, or more specifically, Inversion of Control (IoC) containers or Dependency Injection (DI) containers.

The Inversion of Control (IoC) containers is a factory that is reponsible for providing instance of types that are requested from it. If a given type has declared that it has dependencies, and the container has been configured to provide the dependency types, it will create the dependencies as part of creating the requested instance. Suppose an application designed with the Strategy design pattern. There are designed interfaces and implemented with classes. The IoC conatiner configured to such way that when application is requesting for interface then it provides that interface depedecies i.e class instance, hence class object can be provided to classes without the need for any hard-coded object construction.


ASP.NET Core and DI

The ASP.NET Core itself provides basic built in IoC container that is represented by IserviceProvider interface. It supports constructor depedency injection by default. ASP.NET Core uses DI for for instantiating all its components and services. The container is configured in ConfigureService method of the startup.cs class as this class is entry point to application. We configure the bulit-in and custome services and components that can be used in the entire application life cycle.

The ASP.NET Core services can be configured with the three lifetimes and registration options.

Figure 1: Service lifetime options

Let’s discuss the difference between these lifetime and registration options one by one.

Transient

The AddTransient method is used for the Transient lifetime option. This AddTransient method is used to register services in IoC container and it get instantiated each time when it is accessed. If we have a service which is used multiple places in same request, a new instance would be created each time.

Suppose we have a user service which is used to access user detail and user name so we create two method for these operations. One method is used in action method and another is used on view so there are two new instance create as we requeste for both methods of user service from different part controller and view.

As this method get instantiated on each request that’s why it should be used for lightweight and stateless services.

We create an interface named IUniqueKeyProviderService which has a method signature as per following code snippet.

namespace DIApplication.Service 
{ 
    public interface IUniqueKeyProviderService 
    { 
        int GetUniqueKey(); 
    } 
}

Now, we create a class named UniqueKeyProviderService which has implementation of IUniqueKeyProviderService interface per following code snippet.

namespace DIApplication.Service 
{ 
    public class UniqueKeyProviderService: IUniqueKeyProviderService 
    { 
        public int GetUniqueKey() 
        { 
            return GetHashCode(); 
        } 
    } 
}

Now, we register this IUniqueKeyProviderService service with its depedencies using AddTransient option in the ConfigureServices method of the Startup class as per following code snippet.

public void ConfigureServices(IServiceCollection services) 
        {             
            services.AddMvc(); 
            services.AddTransient<IUniqueKeyProviderService, UniqueKeyProviderService>(); 
        }

Now, we create HomeController to consume method of the IUniqueKeyProviderService service. The following code snippet is for the same.

using DIApplication.Service; 
using Microsoft.AspNetCore.Mvc; 
   
namespace DIApplication.Controllers 
{ 
    public class HomeController : Controller 
    { 
        private readonly IUniqueKeyProviderService uniqueKeyProviderService; 
   
        public HomeController(IUniqueKeyProviderService uniqueKeyProviderService) 
        { 
            this.uniqueKeyProviderService = uniqueKeyProviderService; 
        } 
        
        public IActionResult Index() 
        { 
            int model = uniqueKeyProviderService.GetUniqueKey();             
            return View(model); 
        }         
    } 
}

As per the above code, the controller used constructor dependency injection to inject IUniqueKeyProviderService service. We can also inject IUniqueKeyProviderService service on view to access any method of service. We show unique id on view so create index view as per following code snippet.

@model int 
@inject DIApplication.Service.IUniqueKeyProviderService uniqueKeyProviderService; 
<h2>Transient LifeTime</h2
<h4>Unique key from service instance created in controller: @Model</h4
<h4>Unique key from service instance created in view: @uniqueKeyProviderService.GetUniqueKey()</h4>

Now it runs the application and shows result as follows.

Figure 2: Transient Lifetime

As the result in the above figure shows that a unique key is different from both requests which means each request creates new instance of IUniqueKeyProviderService.

Scoped

The AddScoped method is used for the Scoped lifetime option. This Add Scoped method is used to register services in IoC container. It ptovides service instance once per request. If we have a service which is used multiple places in the same request, only single instance would be created for that.

We have the same view, controller and service. We register this IUniqueKeyProviderService service with its depedencies using AddScoped option in the ConfigureServices method of the Startup class as per following code snippet.

public void ConfigureServices(IServiceCollection services) 
        {             
            services.AddMvc(); 
            services.AddScoped<IUniqueKeyProviderService, UniqueKeyProviderService>(); 
        }

Now run the application and make two subsequent requests and results shown in the below figure.

Figure 3: Scoped Lifetime

As per the above figure, the AddScoped creates a single instance once per request. When we make another request, then it creates another instance of the service.

Singleton

The AddSingleton method is used for the Singleton lifetime option. This AddSingleton method is used to register services in IoC container. It ptovides service instance the first time it is requested. After that every subsequent request will use the same instance. If we have a service which is used multiple places in the same or separate requests, only single instance wll be created for that.

We have same view, controller and service. We register this IUniqueKeyProviderService service with its depedencies using AddSingleton option in the ConfigureServices method of the Startup class as per following code snippet.

public void ConfigureServices(IServiceCollection services) 
        {             
            services.AddMvc(); 
            services.AddSingleton<IUniqueKeyProviderService, UniqueKeyProviderService>(); 
        }

Now, run the application and make two subsequent requests. The result is shown below.

Figure 4: Singleton Lifetime option

As per the above figure, the AddSignleton creates a single instance when requested the frst time. When we make two subsequent requests, then it uses same instance of the service in both requests.

If an application requires singleton behavior, allowing the services container to manage the service's lifetime is recommended instead of implementing the singleton design pattern and managing class object's lifetime in the class itself.


Framework-Provided Services

The ConfigureServices method in the Startup class is responsible for defining the services the application will use, including platform features like Entity Framework Core, ASP.NET Core MVC and ASP.NET Core Identity. The features and middleware provided by ASP.NET, such as MVC, follow a convention of using a single AddServiceName extension method to register all of the services required by that feature such as AddDbContext, AddIdentity, AddMVC and AddAuthorization etc.

  • AddDbContext :This method is used to add application DbContext to the dependency injection.
  • AddIdentity :The identity services are added to the application in the ConfigureServices method.

 

LINK: https://social.technet.microsoft.com/wiki/contents/articles/37218.asp-net-core-overview-of-dependency-injection.aspx

ASP.NET Core Entity Framework Core Code First: CRUD Operations

Introduction


This article introduces how to perform the Create, Read, Update, and Delete (CRUD) operations in ASP.NET Core, using Entity Framework Core. This walk through will use the "Code First" development approach and create a database from model using migration. We can view this article's sample Jumpon TechNet Gallery. I would like to recommend the following wiki article Overview Of ASP.NET Coreso that development environment be prepare for this sample application in ASP.NET Core. We will create a single entity Customer to perform the CRUD operations.

Create Database


First, let's install the Entity Framework Core in our application. As we use SQL Server, install the package for SQL Server database provider. To install database provider, follow the below steps.

  • Tools - NuGet Package Manager - Package Manager Console
  • Run PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer

When we install a package in ASP.NET Core application, then the package installation occurs in the background. We see "(Restoring...)" appeared next to References in Solution Explorer while the installation occurs.

We also use Entity Framework Core as an Object Relationship Mapper (ORM) to perform the CRUD operations. Let's install the Entity Framework Core Tool to maintain the database, using the following procedure.

  • Run PM> Install-Package Microsoft.EntityFrameworkCore.Tools -Pre.
  • Open project.json file.
  • Locate the tools section and add the ef command as shown below.
"tools": {
   "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
   "BundlerMinifier.Core": "2.0.238",
   "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final",
   "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
 }

Now, create two entities - the BaseEntity class that has common properties that will be inherited by each entity, and the Customer. Let's see each entity. The following is a code snippet for the BaseEntity class.

using System;
  
namespace CRUDApplication.DbEntities
{
    public class BaseEntity
    {
        public Int64 Id { get; set; }
        public DateTime AddedDate { get; set; }
        public DateTime ModifiedDate { get; set; }
        public string IPAddress { get; set; }
    }
}
Now, create a Customer entity under the DbEntities folder which inherits from the BaseEntity class. The following is a code for Customer entity.

namespace CRUDApplication.DbEntities
{
    public class Customer:BaseEntity
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string MobileNo { get; set; }
    }
}

Now, let's define the configuration for the Customer entity that will be used when the database table will be created by the entity. The following is the code for Customer mapping entity (CustomerMap.cs).

using Microsoft.EntityFrameworkCore.Metadata.Builders;
  
namespace CRUDApplication.DbEntities
{
    public class CustomerMap
    {
        public CustomerMap(EntityTypeBuilder<Customer> entityBuilder)
        {
            entityBuilder.HasKey(t => t.Id);          
            entityBuilder.Property(t => t.FirstName).IsRequired();
            entityBuilder.Property(t => t.LastName).IsRequired();
            entityBuilder.Property(t => t.Email).IsRequired();
            entityBuilder.Property(t => t.MobileNo).IsRequired();         
        }
    }
}

The EntityTypeBuilder is an important class that allows configuration to be performed for an entity type in a model. This is done using the modelbuilder in an override of the OnModelCreating method. The Constructor of the CustomerMap class uses the Fluent API to map and configure properties in the table. So let's see each method used in the constructor one-by-one.

  1. HasKey(): The Haskey() method configures a primary key on the table.
  2. Property(): The Property method configures attributes for each property belonging to an entity or complex type. It is used to obtain a configuration object for a given property. The options on the configuration object are specific to the type being configured.

Now, it's time to define context class. The ADO.NET Entity Framework Code First development approach requires us to create a data access context class that inherits from the DbContext class so we create a context class CRUDContext (CRUDContext.cs) class. In this class, we override the OnModelCreating() method. This method is called when the model for a context class (CRUDContext) has been initialized, but before the model has been locked down and used to initialize the context such that the model can be further configured before it is locked down. The following is the code snippet for the context class.

using Microsoft.EntityFrameworkCore;
  
namespace CRUDApplication.DbEntities
{
    public class CRUDContext:DbContext
    {
        public CRUDContext(DbContextOptions<CRUDContext> options) : base(options)
        {
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            new CustomerMap(modelBuilder.Entity<Customer>());
        }
    }
}

As the concept of dependency injection is central to the ASP.NET Core application, so we register our context to dependency injection during the application start up. Once we register CRUDContext context as a service to dependency injection, then provide it via constructor to MVC controller. In order for our MVC controllers to make use of CRUDContext, we are going to register it as a service.

  1. Open the appsettings.json file and define connection string here.
    {
      "ConnectionStrings": {
        "DefaultConnection": "Data Source=ADMIN\\SQLEXPRESS;Initial Catalog=ECommerceDb;User ID=sa; Password=sa"
      },
      "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
          "Default": "Debug",
          "System": "Information",
          "Microsoft": "Information"
        }
      }
    }

    It stores application level settings such as connection string, SMTP etc. It's similar to web.config file in ASP.NET.

  2. Open the Startup.cs file and following using statements at the start of the file.
    using Microsoft.EntityFrameworkCore;
    using CRUDApplication.DbEntities;
  3. Now, we can use the AddDbContext method to register it as a service. Locate the ConfigureServices method and add the lines of code for register it to dependency injection as per following code snippet.
    public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc();
                services.AddDbContext<CRUDContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            }

Now we have created a model so time to create a database using migration.

  • Tools -> NuGet Package Manager -> Package Manager Console
  • Run PM> Add-Migration MyFirstMigration to scaffold a migration to create the initial set of tables for our model. If we receive an error stating the term `add-migration' is not recognized as the name of a cmdlet, then close and reopen Visual Studio
  • Run PM> Update-Database to apply the new migration to the database. Because our database doesn't exist yet, it will be created for us before the migration is applied.

Create Application User Interface


Now we proceed to the controller. Create a CustomerController under the Controllers folder of the application. This controller has all ActionResult methods for each user interface of a CRUD operation. We first create a CRUDContext class instance then we inject it in the controller's constructor to get its object. The following is a code snippet for the CustomerController.

using CRUDApplication.DbEntities;
using CRUDApplication.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
  
namespace CRUDApplication.Controllers
{
    public class CustomerController : Controller
    {
        private CRUDContext context;
  
        public CustomerController(CRUDContext context)
        {
            this.context = context;
        }
        [HttpGet]
        public IActionResult Index()
        {
            IEnumerable<CustomerViewModel> model = context.Set<Customer>().ToList().Select(s => new CustomerViewModel
            {
                Id= s.Id,
                Name = $"{s.FirstName} {s.LastName}",
                MobileNo = s.MobileNo,
                Email = s.Email
            });
            return View("Index", model);
        }
  
        [HttpGet]
        public IActionResult AddEditCustomer(long? id)
        {
            CustomerViewModel model = new CustomerViewModel();
            if (id.HasValue)
            {
                Customer customer = context.Set<Customer>().SingleOrDefault(c => c.Id == id.Value);
                if (customer != null)
                {
                    model.Id = customer.Id;
                    model.FirstName = customer.FirstName;
                    model.LastName = customer.LastName;
                    model.MobileNo = customer.MobileNo;
                    model.Email = customer.Email;
                }
            }
            return PartialView("~/Views/Customer/_AddEditCustomer.cshtml", model);
        }
  
        [HttpPost]
        public ActionResult AddEditCustomer(long? id, CustomerViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    bool isNew = !id.HasValue;
                    Customer customer = isNew ? new Customer
                    {
                        AddedDate = DateTime.UtcNow
                    } : context.Set<Customer>().SingleOrDefault(s => s.Id == id.Value);
                    customer.FirstName = model.FirstName;
                    customer.LastName = model.LastName;
                    customer.MobileNo = model.MobileNo;
                    customer.Email = model.Email;
                    customer.IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();
                    customer.ModifiedDate = DateTime.UtcNow;
                    if (isNew)
                    {
                        context.Add(customer);
                    }
                    context.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return RedirectToAction("Index");
        }
  
        [HttpGet]
        public IActionResult DeleteCustomer(long id)
        {
            Customer customer = context.Set<Customer>().SingleOrDefault(c => c.Id == id);
            CustomerViewModel model = new CustomerViewModel
            {
                Name = $"{customer.FirstName} {customer.LastName}"
            };
            return PartialView("~/Views/Customer/_DeleteCustomer.cshtml", model);
        }
        [HttpPost]
        public IActionResult DeleteCustomer(long id, FormCollection form)
        {
            Customer customer = context.Set<Customer>().SingleOrDefault(c => c.Id == id);
            context.Entry(customer).State = Microsoft.EntityFrameworkCore.EntityState.Deleted;
            context.SaveChanges();
            return RedirectToAction("Index");
        }
    }
}

 

We can notice that the Controller takes a CRUDContext as a constructor parameter. ASP.NET dependency injection will take care of passing an instance of CRUDContext into our controller. The controller is developed to handle CURD operation requests for a Customer entity. Now, let's develop the user interface for the CRUD operations. We develop it for the views for adding and editing a customer, a customer listing, customer deletion. Let's see each one by one.

Customer List View


This is the first view when the application is accessed or the entry point of the application is executed. It shows the customer listing as in Figure 1. We display customer data in tabular format and on this view we create links to add a new customer, edit a customer and delete a customer. This view is an index view and the following is a code snippet for index.cshtml under the Customer folder of Views.

@model IEnumerable<CRUDApplication.Models.CustomerViewModel>
@using CRUDApplication.Models
@using CRUDApplication.Code
  
<div class="top-buffer"></div>
<div class="panel panel-primary">
    <div class="panel-heading panel-head">Customers</div>
    <div class="panel-body">
        <div class="btn-group">
            <a id="createEditCustomerModal" data-toggle="modal" asp-action="AddEditCustomer" data-target="#modal-action-customer" class="btn btn-primary">
                <i class="glyphicon glyphicon-plus"></i>  Add Customer
            </a>
        </div>
        <div class="top-buffer"></div>
        <table class="table table-bordered table-striped table-condensed">
            <thead>
                <tr>
                    <th>Name</th>                  
                    <th>Email</th>
                    <th>Mobile No</th>
                    <th>Action</th>
                </tr>
            </thead>
            <tbody>
                @foreach (var item in Model)
                {
                    <tr>
                        <td>@Html.DisplayFor(modelItem => item.Name)</td>
                        <td>@Html.DisplayFor(modelItem => item.Email)</td>
                        <td>@Html.DisplayFor(modelItem => item.MobileNo)</td>                     
                        <td>
                            <a id="editCustomerModal" data-toggle="modal" asp-action="AddEditCustomer" asp-route-id= "@item.Id" data-target="#modal-action-customer"
                               class="btn btn-info">
                                <i class="glyphicon glyphicon-pencil"></i>  Edit
                            </a>
                            <a id="deleteCustomerModal" data-toggle="modal" asp-action="DeleteCustomer" asp-route-id= "@item.Id" data-target="#modal-action-customer" class="btn btn-danger">
                                <i class="glyphicon glyphicon-trash"></i>  Delete
                            </a>
                        </td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
</div>
@Html.Partial("_Modal", new BootstrapModel { ID = "modal-action-customer", AreaLabeledId = "modal-action-customer-label", Size = ModalSize.Small })
@section scripts
{
    <script src="~/js/customer-index.js" asp-append-version="true"></script>
}

When we run the application and call the index() action with a HttpGet request, then we get all the customers listed in the UI as in Figure 1. This UI has options for CRUD operations.

Figure 1: Customer Listing UI

Create / Edit Customer View


We create a common view to create and edit a customer so we create a single customer view model. The following code snippet for CustomerViewModel.cs.

using System.ComponentModel.DataAnnotations;
  
namespace CRUDApplication.Models
{
    public class CustomerViewModel
    {
        public long Id { get; set; }
        [Display(Name="First Name")]
        public string FirstName { get; set; }
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        [Display(Name = "Mobile No")]
        public string MobileNo { get; set; }
    }
}

We show form in the bootstrap modal popup and submit using ajax post; that's why we create a javascript file which contains a method for removing loaded data.

(function ($) {
    function Customer() {
        var $this = this;
  
        function initilizeModel() {         
            $("#modal-action-customer").on('loaded.bs.modal', function (e) {             
                }).on('hidden.bs.modal', function (e) {                 
                    $(this).removeData('bs.modal');
                });          
        }     
        $this.init = function () {
            initilizeModel();
        }
    }
    $(function () {
        var self = new Customer();
        self.init();      
    })
}(jQuery))

Now, define a create/edit customer partial view. The following is the code snippet for _AddEditCustomer.cshtml.

@model CRUDApplication.Models.CustomerViewModel
@using CRUDApplication.Models
  
<form asp-action="AddEditCustomer" role="form">
    @await Html.PartialAsync("_ModalHeader", new ModalHeader { Heading = String.Format("{0} Customer", @Model.Id == 0 ? "Add" : "Edit") })
     
    <div class="modal-body form-horizontal">
        <div class="form-group">
            <label asp-for="FirstName" class="col-lg-3 col-sm-3 control-label"></label>         
            <div class="col-lg-6">
                <input asp-for="FirstName" class="form-control" />              
            </div>
        </div>
        <div class="form-group">
            <label asp-for="LastName" class="col-lg-3 col-sm-3 control-label"></label>
            <div class="col-lg-6">
                <input asp-for="LastName" class="form-control" />                             
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Email" class="col-lg-3 col-sm-3 control-label"></label>
            <div class="col-lg-6">
                <input asp-for="Email" class="form-control" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="MobileNo" class="col-lg-3 col-sm-3 control-label"></label>
            <div class="col-lg-6">
                <input asp-for="MobileNo" class="form-control" />
            </div>
        </div>      
    </div>
  @await Html.PartialAsync("_ModalFooter", new ModalFooter { })
</form>

Now, run the application and click on Edit button of listing which calls AddEditCustomer action method, then we get the UI as in Figure 2 to edit a customer.

Figure 2: Edit a Customer UI

Delete A Customer


To delete a customer, we follow the process of clicking on the Delete button that exists in the Customer listing data then a modal popup shows to ask "Are you want to delete xxx?" after clicking on the Delete button that exists in the popup view such as in Figure 3 then it makes a HttpPost request that calls the DeleteCustomer() action method and deletes the customer. The following is a code snippet for _DeleteCustomer.cshtml.

@model CRUDApplication.Models.CustomerViewModel
@using CRUDApplication.Models
  
@using (Html.BeginForm())
{
    @Html.Partial("_ModalHeader", new ModalHeader { Heading = "Delete Customer" })
  
    <div class="modal-body form-horizontal">
        Are you want to delete @Model.Name?
    </div>
    @Html.Partial("_ModalFooter", new ModalFooter { SubmitButtonText = "Delete" })
}

Now, run the application and click on Delete button of listing which calls DeleteCustomer action method, then we get the UI as in Figure 3 to delete a customer.

Figure 3: Delete Confirmation

Conclusion

This article introduced CRUD operations in ASP.NET Core using Entity Framework Core with "code first" development approach. We used bootstrap CSS and JavaScript for the user interface design in this application.


 

LINK: https://social.technet.microsoft.com/wiki/contents/articles/36046.asp-net-core-entity-framework-core-code-first-crud-operations.aspx

Onion Architecture In ASP.NET Core MVC Detail

 

Introduction

The Onion Architecture term was coined by Jeffrey Palermo in 2008. This architecture provides a better way to build applications for better testability, maintainability, and dependability on the infrastructures like databases and services. This architecture's main aim is to address the challenges faced with 3-tier architecture or n-tier architecture, and to provide a solution for common problems, like coupling and separation of concerns. There are two types of coupling - tight coupling and loose coupling.

Tight Coupling

When a class is dependent on a concrete dependency, it is said to be tightly coupled to that class. A tightly coupled object is dependent on another object; that means changing one object in a tightly coupled application, often requires changes to a number of other objects. It is not difficult when an application is small but in an enterprise level application, it is too difficult to make the changes.

Loose Coupling

It means two objects are independent and an object can use another object without being dependent on it. It is a design goal that seeks to reduce the inter-dependencies among components of a system with the goal of reducing the risk that changes in one component will require changes in any other component.


Advantages of Onion Architecture

There are several advantages of the Onion Architecture, as listed below.

  1. It provides better maintainability as all the codes depend on layers or the center.
  2. It provides better testability as the unit test can be created for separate layers without an effect of other modules of the application.
  3. It develops a loosely coupled application as the outer layer of the application always communicates with inner layer via interfaces.
  4. Any concrete implantation would be provided to the application at run time
  5. Domain entities are core and center part. It can have access to both database and UI layers.
  6. The internal layers never depend on external layer. The code that may have changed should be part of an external layer.

Why Onion Architecture

There are several traditional architectures, like 3-tier architecture and n-tier architecture, all having their own pros and cons. All these traditional architectures have some fundamental issues, such as - tight coupling and separation of concerns. The Model-View-Controller is the most commonly used web application architecture, these days. It solves the problem of separation of concern as there is a separation between UI, business logic, and data access logic. The View is used to design the user interface. The Model is used to pass the data between View and Controller on which the business logic performs any operations. The Controller is used to handle the web request by action methods and returns View accordingly. Hence, it solves the problem of separation of concern while the Controller is still used to database access logic. In essence, MVC solves the separation of concern issue but the tight coupling issue still remains.

On the other hand, Onion Architecture addresses both the separation of concern and tight coupling issues. The overall philosophy of the Onion Architecture is to keep the business logic, data access logic, and model in the middle of the application and push the dependencies as far outward as possible means all coupling towards to center.


Onion Architecture Layers

This architecture relies heavily on the Dependency Inversion Principle. The UI communicates to business logic through interfaces. It has four layers, as shown in figure 1.

  1. Domain Entities Layer
  2. Repository Layer
  3. Service Layer
  4. UI (Web/Unit Test) Layer

Figure 1: Onion Architecture Layers

These layers are towards to center. The center part is the Domain entities which represent the business and behavior objects. These layers can vary but the domain entities layer is always part of the center. The other layer defines more behavior of an object. Let’s see each layer one by one.

Domain Entities Layer

It is the center part of the architecture. It holds all application domain objects. If an application is developed with ORM entity framework then this layer holds POCO classes (Code First) or Edmx (Database First) with entities. These domain entities don't have any dependencies.

Repository Layer

The layer is intended to create an Abstraction layer between the Domain entities layer and Business Logic layer of an application. It is a data access pattern that prompts a more loosely coupled approach to data access. We create a generic repository, which queries the data source for the data, maps the data from the data source to a business entity and persists changes in the business entity to the data source.

Service Layer

The layer holds interfaces which are used to communicate between the UI layer and repository layer. It holds business logic for an entity so it’s called business logic layer as well.

UI Layer

It’s the most external layer. It could be the web application, Web API or Unit Test project. This layer has an implementation of the Dependency Inversion Principle so that application builds a loosely coupled application. It communicates to internal layer via interfaces.


Onion Architecture Project Structure

To implement the Onion architecture, we develop an ASP.NET Core application. This application performs CRUD operations on entities. The application holds four projects as per figure 2. Each project represents a layer in onion architecture.

Figure 2: Application projects structure

There are four projects in which three are class library projects and one is web application project. Let’s see each project mapping with onion architecture layers.

OA.Data

It is a class library project. It holds POCO classes along with configuration classes. It represents the Domain Entities layer of the onion architecture. These classes are used to create database tables. It’s a core and central part of the application.

OA.Repo

It is a second class library project. It holds generic repository class with its interface implementation. It also holds DbContext class. The Entity Framework Code First data access approach needs to create a data access context class that inherits from the DbContext class. This project represents the Repository layer of the onion architecture.

OA.Service

It is a third class library project. It holds business logic and interfaces. These interfaces communicate between UI and data access logic. As it communicates via interfaces, it builds applications that are loosely coupled. This project represents the Service layer of the onion architecture.

OA.Web

It is an ASP.NET Core Web application in this sample but it could be Unit Test or Web API project. It is the most external part of an application by which the end user can interact with the application. It builds loosely coupled applications with in-built dependency injection in ASP.NET Core. It represents the UI layer of the onion architecture.


Implement Onion Architecture

To implement the Onion Architecture in the ASP.NET Core application, create four projects as described in the above section. These four projects represent four layers of the onion architecture. Let’s see each one by one.

Domain Entities Layer

The Entities Domain layer is a core and central part of the architecture. So first, we create "OA.Data" project to implement this layer. This project holds POCO class and fluent API configuration for this POCO classes.

There is an unsupported issue of EF Core 1.0.0-preview2-final with "NETStandard.Library": "1.6.0". Thus, we have changed the target framework to netstandard1.6 > netcoreapp1.0. We modify the project.json file of OA.Data project to implement the Entity Framework Core in this class library project. Thus, the code snippet, mentioned below, is used for the project.json file after modification.

{
  "dependencies": {
    "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
  },
  "frameworks": {
    "netcoreapp1.0": {
      "imports": [ "dotnet5.6", "portable-net45+win8" ]
    }
  },
  "tools": {
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
  },
  "version": "1.0.0-*"
}

This Application uses the Entity Framework Code First approach, so the project OA.Data contains entities that are required in the application's database. The OA.Data project holds three entities, one is the BaseEntity class that has common properties that will be inherited by each entity. The code snippet, mentioned below is the BaseEntity class.

using System;
 
namespace OA.Data
{
    public class BaseEntity
    {
        public Int64 Id { get; set; }
        public DateTime AddedDate { get; set; }
        public DateTime ModifiedDate { get; set; }
        public string IPAddress { get; set; }
    }
}

There are two more entities, one is User and the another one is UserProfile. Both entities have a one to one relationship, as shown below.

Figure 3: One to One User-UserProfile relationship

Now, we create an User entity, which is inherited from BaseEntity class. The code snippet, mentioned below is for the User entity.

namespace OA.Data
{
    public class User:BaseEntity
    {
        public string UserName { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public virtual UserProfile UserProfile { get; set; }
    }
}

Now, we define the configuration for the User entity that will be used when the database table will be created by the entity. The following is a code snippet for the User mapping entity (UserMap.cs).

using Microsoft.EntityFrameworkCore.Metadata.Builders;
 
namespace OA.Data
{
    public class UserMap
    {
        public UserMap(EntityTypeBuilder<User> entityBuilder)
        {
            entityBuilder.HasKey(t => t.Id);
            entityBuilder.Property(t => t.Email).IsRequired();
            entityBuilder.Property(t => t.Password).IsRequired();
            entityBuilder.Property(t => t.Email).IsRequired();
            entityBuilder.HasOne(t => t.UserProfile).WithOne(u => u.User).HasForeignKey<UserProfile>(x => x.Id);
        }
    }
}

Now, we create a UserProfile entity, which inherits from the BaseEntity class. The code snippet, mentioned below is the UserProfile entity.

namespace OA.Data
{
    public class UserProfile:BaseEntity
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Address { get; set; }
        public virtual User User { get; set; }
    }
}

Now, we define the configuration for the UserProfile entity that will be used when the database table will be created by the entity. The code snippet is mentioned below for the UserProfile mapping entity (UserProfileMap.cs).

using Microsoft.EntityFrameworkCore.Metadata.Builders;
 
namespace OA.Data
{
    public class UserProfileMap
    {
        public UserProfileMap(EntityTypeBuilder<UserProfile> entityBuilder)
        {
            entityBuilder.HasKey(t => t.Id);
            entityBuilder.Property(t => t.FirstName).IsRequired();
            entityBuilder.Property(t => t.LastName).IsRequired();
            entityBuilder.Property(t => t.Address); 
        }
    }
}

Repository Layer

Now we create a second layer of the onion architecture which is repository layer. To build this layer, we create one more class library project named OA.Repo. This project holds both the repository and data context classes.

The OA.Repo project contains DataContext. The ADO.NET Entity Framework Code First data access approach needs to create a data access context class that inherits from the DbContext class, so we create a context class ApplicationContext (ApplicationContext.cs) class.

In this class, we override the OnModelCreating() method. This method is called when the model for a context class (ApplicationContext) has been initialized, but before the model has been locked down and used to initialize the context such that the model can be further configured before it is locked down. The following is the code snippet for the context class.

using Microsoft.EntityFrameworkCore;
using OA.Data;
 
namespace OA.Repo
{
    public class ApplicationContext : DbContext
    {
        public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options)
        {
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            new UserMap(modelBuilder.Entity<User>());
            new UserProfileMap(modelBuilder.Entity<UserProfile>());
        }
    }
}

The DbContext must have an instance of DbContextOptions in order to execute. We will use dependency injection, so we pass options via constructor dependency injection. ASP.NET Core is designed from the ground to support and leverage dependency injection. Thus, we create generic repository interface for the entity operations, so that we can develop loosely coupled application. The code snippet, mentioned below is the IRepository interface.

using OA.Data;
using System.Collections.Generic;
 
namespace OA.Repo
{
    public interface IRepository<T> where T : BaseEntity
    {
        IEnumerable<T> GetAll();
        T Get(long id);
        void Insert(T entity);
        void Update(T entity);
        void Delete(T entity);
        void Remove(T entity);
        void SaveChanges();
    }
}

Now, let's create a repository class to perform database operations on the entity, which implements IRepository. This repository contains a parameterized constructor with a parameter as Context, so when we create an instance of the repository, we pass a context so that the entity has the same context. The code snippet is mentioned below for the Repository class under OA.Repo project.

using Microsoft.EntityFrameworkCore;
using OA.Data;
using System;
using System.Collections.Generic;
using System.Linq;
 
namespace OA.Repo
{
    public class Repository<T> : IRepository<T> where T : BaseEntity
    {
        private readonly ApplicationContext context;
        private DbSet<T> entities;
        string errorMessage = string.Empty;
 
        public Repository(ApplicationContext context)
        {
            this.context = context;
            entities = context.Set<T>();
        }
        public IEnumerable<T> GetAll()
        {
            return entities.AsEnumerable();
        }
 
        public T Get(long id)
        {
            return entities.SingleOrDefault(s => s.Id == id);
        }
        public void Insert(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            entities.Add(entity);
            context.SaveChanges();
        }
 
        public void Update(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            context.SaveChanges();
        }
 
        public void Delete(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            entities.Remove(entity);
            context.SaveChanges();
        }
        public void Remove(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            entities.Remove(entity);           
        }
 
        public void SaveChanges()
        {
            context.SaveChanges();
        }
    }
}

We developed entity and context which are required to create a database but we will come back to this after creating the two more projects.

Service Layer

Now we create the third layer of the onion architecture which is service layer. To build this layer, we create one more class library project named OA.Service. This project holds interfaces and classes which have an implementation of interfaces. This layer is intended to build loosely coupled applications. This layer communicates with both Web applications and repository projects.

We create an interface named IUserService. This interface holds all methods signature which access by external layer for the User entity. The following code snippet is for the same (IUserService.cs).

using OA.Data;
using System.Collections.Generic;
 
namespace OA.Service
{
    public  interface IUserService
    {
        IEnumerable<User> GetUsers();
        User GetUser(long id);
        void InsertUser(User user);
        void UpdateUser(User user);
        void DeleteUser(long id);
    }
}

Now, this IUserService interface implements on a class named UserService. This UserService class holds all the operations for User entity. The following code snippet is for the same(UserService.cs).

using OA.Data;
using OA.Repo;
using System.Collections.Generic;
 
namespace OA.Service
{
    public class UserService:IUserService
    {
        private IRepository<User> userRepository;
        private IRepository<UserProfile> userProfileRepository;
 
        public UserService(IRepository<User> userRepository, IRepository<UserProfile> userProfileRepository)
        {
            this.userRepository = userRepository;
            this.userProfileRepository = userProfileRepository;
        }
 
        public IEnumerable<User> GetUsers()
        {
            return userRepository.GetAll();
        }
 
        public User GetUser(long id)
        {
            return userRepository.Get(id);
        }
 
        public void InsertUser(User user)
        {
            userRepository.Insert(user);
        }
        public void UpdateUser(User user)
        {
            userRepository.Update(user);
        }
 
        public void DeleteUser(long id)
        {           
            UserProfile userProfile = userProfileRepository.Get(id);
            userProfileRepository.Remove(userProfile);
            User user = GetUser(id);
            userRepository.Remove(user);
            userRepository.SaveChanges();
        }
    }
}

We create one more interface named IUserProfileService. This interface holds method signature which is accessed by the external layer for the UserProfile entity. The following code snippet is for the same (IUserProfileService.cs).

using OA.Data;
 
namespace OA.Service
{
    public interface IUserProfileService
    {
        UserProfile GetUserProfile(long id);
    }
}

Now, this IUserProfileService interface implements on a class named UserProfileService. This UserProfileService class holds the operation for the UserProfile entity. The following code snippet is for the same(UserProfileService.cs).

using OA.Data;
using OA.Repo;
 
namespace OA.Service
{
    public class UserProfileService: IUserProfileService
    {
        private IRepository<UserProfile> userProfileRepository;
 
        public UserProfileService(IRepository<UserProfile> userProfileRepository)
        {          
            this.userProfileRepository = userProfileRepository;
        }
 
        public UserProfile GetUserProfile(long id)
        {
            return userProfileRepository.Get(id);
        }
    }
}

UI Layer

Now, we create the external layer of the onion architecture which is UI layer. The end user interacts with the application by this layer. To build this layer, we create an ASP.NET Core MVC web application named OA.Web. This layer communicates to service layer projects. This project contains the user interface for both user and user profile entities database operations and the controller to do these operations.

As the concept of dependency injection is central to the ASP.NET Core application, we register context, repository, and service to the dependency injection during the application start up. Thus, we register these as a Service in the ConfigureServices method in the StartUp class as per following code snippet.

public void ConfigureServices(IServiceCollection services)
     {          
         services.AddMvc();
         services.AddDbContext<ApplicationContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
         services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
         services.AddTransient<IUserService, UserService>();
         services.AddTransient<IUserProfileService, UserProfileService>();
     }

Here, the DefaultConnection is connection string which defined in appsettings.json file as per following code snippet.

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=DESKTOP-RG33QHE;Initial Catalog=OADb;User ID=sa; Password=****"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

Now, we have configured settings to create database, so we have time to create a database, using migration. We must choose the OA.Repo project in the Package Manager console during the performance of the steps, mentioned below.

  1. Tools -> NuGet Package Manager -> Package Manager Console
  2. Run PM> Add-Migration MyFirstMigration to scaffold a migration to create the initial set of tables for our model. If we receive an error stating the term `add-migration' is not recognized as the name of a cmdlet, then close and reopen Visual Studio.
  3. Run PM> Update-Database to apply the new migration to the database. Because our database doesn't exist yet, it will be created for us before the migration is applied.

Create Application User Interface

Now, we proceed to the controller. We create controller named UserController under the Controllers folder of the application. It has all ActionResult methods for end user interface of operations. We create both IUserService and IUserProfile interface instances; then we inject these in the controller's constructor to get its object. The following is a partial code snippet for the UserController in which service interfaces are injected, using constructor dependency injection.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using OA.Service;
using OA.Web.Models;
using OA.Data;
using Microsoft.AspNetCore.Http;
 
namespace OA.Web.Controllers
{
    public class UserController : Controller
    {
        private readonly IUserService userService;
        private readonly IUserProfileService userProfileService;
 
        public UserController(IUserService userService, IUserProfileService userProfileService)
        {
            this.userService = userService;
            this.userProfileService = userProfileService;
        }
    }
}

We can notice that Controller takes both IUserService and IUserProfileService as a constructor parameters. The ASP.NET Core dependency injection will take care of passing an instance of these services into UserController. The controller is developed to handle operations requests for both User and UserProfile entities. Now, let's develop the user interface for the User Listing, Add User, Edit User and Delete User. Let's see each one by one.

User List View

This is the first view when the application is accessed or the entry point of the application is executed. It shows the author listing as in Figure 4. The user data is displayed in a tabular format and on this view, it has linked to add a new user, edit a user and delete a user.

To pass data from controller to view, create named UserViewModel view model, as per the code snippet, mentioned below. This view model is also used for adding or editing a user.

using Microsoft.AspNetCore.Mvc;
using System;
using System.ComponentModel.DataAnnotations;
 
namespace OA.Web.Models
{
    public class UserViewModel
    {
        [HiddenInput]
        public Int64 Id { get; set; }
        [Display(Name = "First Name")]
        public string FirstName { get; set; }
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        [Display(Name = "User Name")]
        public string UserName { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        [Display(Name = "Added Date")]
        public DateTime AddedDate { get; set; }
    }
}

Now, we create action method, which returns an index view with the data. The code snippet of Index action method in UserController is mentioned below.

[HttpGet]
        public IActionResult Index()
        {
            List<UserViewModel> model = new List<UserViewModel>();
            userService.GetUsers().ToList().ForEach(u =>
            {
                UserProfile userProfile = userProfileService.GetUserProfile(u.Id);
                UserViewModel user = new UserViewModel
                {
                    Id = u.Id,
                    Name = $"{userProfile.FirstName} {userProfile.LastName}",
                    Email = u.Email,
                    Address = userProfile.Address
                };
                model.Add(user);
            });
 
            return View(model);
        }

Now, we create an index view, as per the code snippet, mentioned below under the User folder of views.

@model IEnumerable<UserViewModel>
@using OA.Web.Models
@using OA.Web.Code
 
<div class="top-buffer"></div>
<div class="panel panel-primary">
    <div class="panel-heading panel-head">Users</div>
    <div class="panel-body">
        <div class="btn-group">
            <a id="createEditUserModal" data-toggle="modal" asp-action="AddUser" data-target="#modal-action-user"class="btn btn-primary">
                <i class="glyphicon glyphicon-plus"></i>  Add User
            </a>
        </div>
        <div class="top-buffer"></div>
        <table class="table table-bordered table-striped table-condensed">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Email</th>
                    <th>Address</th>
                    <th>Action</th>
                </tr>
            </thead>
            <tbody>
                @foreach (var item in Model)
                {
                    <tr>
                        <td>@Html.DisplayFor(modelItem => item.Name)</td>
                        <td>@Html.DisplayFor(modelItem => item.Email)</td>
                        <td>@Html.DisplayFor(modelItem => item.Address)</td>
                        <td>
                            <a id="editUserModal" data-toggle="modal" asp-action="EditUser" asp-route-id="@item.Id"data-target="#modal-action-user"
                               class="btn btn-info">
                                <i class="glyphicon glyphicon-pencil"></i>  Edit
                            </a>                        
                            <a id="deleteUserModal" data-toggle="modal" asp-action="DeleteUser" asp-route-id="@item.Id"data-target="#modal-action-user" class="btn btn-danger">
                                <i class="glyphicon glyphicon-trash"></i>  Delete
                            </a>
                        </td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
</div>
 
@Html.Partial("_Modal", new BootstrapModel { ID = "modal-action-user", AreaLabeledId = "modal-action-user-label", Size = ModalSize.Large })
 
@section scripts
{
    <script src="~/js/user-index.js" asp-append-version="true"></script>
}

It shows all forms in the Bootstrap model popup so create the user - index.js file as per the following code snippet.

(function ($) {
    function User() {
        var $this = this;
 
        function initilizeModel() {
            $("#modal-action-user").on('loaded.bs.modal', function (e) {
 
            }).on('hidden.bs.modal', function (e) {
                $(this).removeData('bs.modal');
            });
        }
        $this.init = function () {
            initilizeModel();
        }
    }
    $(function () {
        var self = new User();
        self.init();
    })
}(jQuery))

When the application runs and calls the index() action method from UserController with a HttpGet request, it gets all the users listed in the UI, as shown in Figure 4.

Figure 4: User listing

Add User

To pass the data from UI to a controller to add a user, use same view model named UserViewModel. The AuthorController has an action method named AddUser which returns the view to add a user. The code snippet mentioned below is for same action method for both GET and Post requests.

[HttpGet]
        public ActionResult AddUser()
        {
            UserViewModel model = new UserViewModel();
 
            return PartialView("_AddUser", model);
        }
 
        [HttpPost]
        public ActionResult AddUser(UserViewModel model)
        {
            User userEntity = new User
            {
                UserName = model.UserName,
                Email = model.Email,
                Password = model.Password,
                AddedDate = DateTime.UtcNow,
                ModifiedDate = DateTime.UtcNow,
                IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString(),
                UserProfile = new UserProfile
                {
                    FirstName = model.FirstName,
                    LastName = model.LastName,
                    Address = model.Address,
                    AddedDate = DateTime.UtcNow,
                    ModifiedDate = DateTime.UtcNow,
                    IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString()
                }
            };
            userService.InsertUser(userEntity);
            if (userEntity.Id > 0)
            {
                return RedirectToAction("index");
            }
            return View(model);
        }

The GET request for the AddUser action method returns _AddUser partial view; the code snippet follows under the User folder of views.

@model UserViewModel
@using OA.Web.Models
 
<form asp-action="AddUser" role="form">
    @await Html.PartialAsync("_ModalHeader", new ModalHeader { Heading = "Add User" })
    <div class="modal-body form-horizontal">
        <div class="row">
            <div class="col-lg-6">
                <div class="form-group">
                    <label asp-for="FirstName" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input asp-for="FirstName" class="form-control" />
                    </div>
                </div>
                <div class="form-group">
                    <label asp-for="LastName" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input asp-for="LastName" class="form-control" />
                    </div>
                </div>
                <div class="form-group">
                    <label asp-for="Email" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input asp-for="Email" class="form-control" />
                    </div>
                </div>
            </div>
            <div class="col-lg-6">
                <div class="form-group">
                    <label asp-for="UserName" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input asp-for="UserName" class="form-control" />
                    </div>
                </div>
                <div class="form-group">
                    <label asp-for="Password" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input type="password" asp-for="Password" class="form-control" />
                    </div>
                </div>
                <div class="form-group">
                    <label asp-for="Address" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input asp-for="Address" class="form-control" />
                    </div>
                </div>
            </div>
        </div>
    </div>
    @await Html.PartialAsync("_ModalFooter", new ModalFooter { })
</form>

When the application runs and you click on the Add User button, it makes a GET request for the AddUser() action; add a user screen, as shown in Figure 5.

Figure 5: Add User screen

Edit User

To pass the data from UI to controller to edit a user, use same view model named UserViewModel. The UserController has an action method named EditUser, which returns view to edit a user. The code snippet mentioned below is for the same action method for both GET and Post requests.

public ActionResult EditUser(int? id)
        {
            UserViewModel model = new UserViewModel();
            if (id.HasValue && id != 0)
            {
                User userEntity = userService.GetUser(id.Value);
                UserProfile userProfileEntity = userProfileService.GetUserProfile(id.Value);
                model.FirstName = userProfileEntity.FirstName;
                model.LastName = userProfileEntity.LastName;
                model.Address = userProfileEntity.Address;
                model.Email = userEntity.Email;
            }
            return PartialView("_EditUser", model);
        }
 
        [HttpPost]
        public ActionResult EditUser(UserViewModel model)
        {
            User userEntity = userService.GetUser(model.Id);
            userEntity.Email = model.Email;
            userEntity.ModifiedDate = DateTime.UtcNow;
            userEntity.IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();
            UserProfile userProfileEntity = userProfileService.GetUserProfile(model.Id);
            userProfileEntity.FirstName = model.FirstName;
            userProfileEntity.LastName = model.LastName;
            userProfileEntity.Address = model.Address;
            userProfileEntity.ModifiedDate = DateTime.UtcNow;
            userProfileEntity.IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();
            userEntity.UserProfile = userProfileEntity;
            userService.UpdateUser(userEntity);
            if (userEntity.Id > 0)
            {
                return RedirectToAction("index");
            }
            return View(model);
        }

The GET request for the EditUser action method returns _EditUser partial view, where code snippet follows under the User folder of views.

@model UserViewModel
@using OA.Web.Models
 
<form asp-action="EditUser" role="form">
    @await Html.PartialAsync("_ModalHeader", new ModalHeader { Heading = "Edit User" })
    <div class="modal-body form-horizontal">
        <div class="row">           
            <input asp-for="Id" />
                <div class="form-group">
                    <label asp-for="FirstName" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input asp-for="FirstName" class="form-control" />
                    </div>
                </div>
                <div class="form-group">
                    <label asp-for="LastName" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input asp-for="LastName" class="form-control" />
                    </div>
                </div>
                <div class="form-group">
                    <label asp-for="Email" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input asp-for="Email" class="form-control" />
                    </div>
                </div>        
           
                <div class="form-group">
                    <label asp-for="Address" class="col-lg-3 col-sm-3 control-label"></label>
                    <div class="col-lg-6">
                        <input asp-for="Address" class="form-control" />
                    </div>
                </div>
             
        </div>
    </div>
    @await Html.PartialAsync("_ModalFooter", new ModalFooter { })
</form>

When the application runs and you click on the Edit button in the User listing, it makes a GET request for the EditUser() action, then the edit user screen is shown in Figure 6.

Figure 6: Edit User

Delete User

The UserController has an action method named DeleteUser, which returns the view to delete a user. The code snippet mentioned below is for the same action method for both GET and Post requests.

[HttpGet]
        public PartialViewResult DeleteUser(int id)
        {
            UserProfile userProfile = userProfileService.GetUserProfile(id);
            string name = $"{userProfile.FirstName} {userProfile.LastName}";
            return PartialView("_DeleteUser", name);
        }
 
        [HttpPost]
        public ActionResult DeleteUser(long id, FormCollection form)
        {
            userService.DeleteUser(id);         
            return RedirectToAction("Index");
        }

The GET request for the DeleteUser action method returns _DeleteUser partial View. The code snippet mentioned below is under the User folder of Views.

@model string
@using OA.Web.Models
 
<form asp-action="DeleteUser" role="form">
    @Html.Partial("_ModalHeader", new ModalHeader { Heading = "Delete User" })
 
    <div class="modal-body form-horizontal">
        Are you want to delete @Model?
    </div>
    @Html.Partial("_ModalFooter", new ModalFooter { SubmitButtonText = "Delete" })
</form>

When the application runs and a user clicks on the "Delete" button in the user listing, it makes a GET request for the DeleteUser() action, then the delete user screen is shown, as below.

Figure 7: Delete User
 

 

ASP.NET Core MVC: Authentication and Role Based Authorisation with Identity

Introduction

A Visual Studio 2015 project which shows how to implement authentication and role based authorization with ASP.NET identity in the ASP.NET Core MVC application.

The code illustrates the following topics:

  1. Listings, create, update and delete application roles.
  2. Listings, create, update and delete application users.
  3. Assign and update an application role to the application user.
  4. Login and Logout functionality.
  5. Role-based authorization.
  6. Access denied implemented for unauthorized users.
  7. Remember me for the authenticate user.
  8. Show username of the authenticated user.
  9. Custom application user and role classes.

Getting Started

To build and run this sample as-is, you must have Visual Studio 2015 installed. In most cases you can run the application by following these steps:

  1. Download and extract the .zip file.
  2. Open the solution file in Visual Studio.
  3. Change connection string in the appsettings.json file of the web application.
  4. Run the following command for migration and create database.
    • Tools –> NuGet Package Manager –> Package Manager Console
    • PM> Add-Migration MyFirstMigration
    • PM> Update-Database
  5. Run the application.

Running the Sample

To run the sample, hit F5 or choose the Debug | Start Debugging menu command. You will see the role list screen. From this screen you have role listing screen as shown in below figure. There are also top menu for the ‘Role’ when clicks on that then same screen opens.

Figure 1: Role listing

Now click on “Add Role” button to add new application role in the application as per following screen.

Figure 2: Add Application Role

As per figure 1, Delete button uses to delete individual application role as per following figure.

Figure 3: Delete Application Role

Now clicks on User menu on the top and shows the application users listing as shown in below figure.

Figure 4: Application User Listing

Now click on “Add User” button to add new application user in the application as per following screen.

Figure 5: Add Application User

As per figure 4, Edit button uses to edit individual application user as per following figure.

Figure 6: Edit Application User

As per figure 4, Delete button uses to delete individual application role as per following figure.

Figure 7: Delete Application User

Now click on Log In menu button on top the right corner and login with following screen.

Figure 8: User Login Screen

Clicks on ‘Log In’ button as role based show following screen.The authenticate user must have 'User' role to access this screen.

Figure 9: Welcome Screen After Authorisation

If authenticate user is not authorised then shown following screen.

Figure 10: UnAuthorised Screen

Source Code Overview

Most of folders play same role as in MVC application but there are following more folder and files.

  1. wwwroot: It holds static js and css files.
  2. appsettings.json:It holds database connection string.
  3. Migrations: It holds database migration files.
  4. ApplicationUser: Custom identity User Class.
  5. ApplicationRole: Custome Identity Role Class.

LINK: https://code.msdn.microsoft.com/ASPNET-Core-MVC-Authenticat-ef5942f5#content