TRUNGTQ

Think Big, Act Small, Fail Fast and Learn Rapidly

NAVIGATION - SEARCH

Using ASP.NET Core, Entity Framework Core and ASP.NET Boilerplate to Create NLayered Web Application (Part I)

A step by step guide to create a web application based on ASP.NET Core, Entity Framework Core and ASP.NET Boilerplate frameworks with automated tests.

Contents

  • Introduction
    • Prerequirements
  • Create the Application
  • Developing the Application
    • Creating a Task Entity
    • Adding Task to DbContext
    • Creating the First Database Migration
    • Creating the Database
    • Task Application Service
    • Testing the TaskAppService
    • Task List View
      • Adding a New Menu Item
      • Creating the TaskController and ViewModel
      • Task List Page
      • Localization
      • Filtering Tasks
    • Automated Testing Task List Page
  • More
  • Article History

Introduction

This is first part of the "Using ASP.NET Core, Entity Framework Core and ASP.NET Boilerplate to Create NLayered Web Application" article series. See other parts:

In this article, I'll show to create a simple layered web application using the following tools:

We will also use Log4Net and AutoMapper which are included in ABP startup template by default. We will use the following techniques:

The project will be developed here is a simple task management application where tasks can be assigned to people. Instead of developing the application layer by layer, I will go to vertical and change the layers as the application grows. While the application grows, I will introduce some features of ABP and other frameworks as needed.

Prerequirements

Following tools should be installed in your machine to be able to run/develop the sample application:

Create the Application

I used ABP's startup template (http://www.aspnetboilerplate.com/Templates) to create a new web application named "Acme.SimpleTaskApp" (company name, "Acme" here, is optional while creating templates):

ABP startup template creation

It creates a layered solution as shown below: 

Startup template projects

It includes 6 projects starting with the name that I entered as the project name:

  • .Core project is for domain/business layer (entities, domain services...)
  • .Application project is for application layer (DTOs, application services...)
  • .EntityFramework project is for EF Core integration (abstracts EF Core from other layers).
  • .Web project is for ASP.NET MVC layer.
  • .Tests project is for unit and integration tests (up to application layer, excluding web layer)
  • .Web.Tests project is for ASP.NET Core integrated tests (complete integration test for server side).

When you run the application, you can see the user interface of the template:

Template Home Page

It contains a top menu, empty Home and About pages and a language switch dropdown.

Developing the Application

Creating a Task Entity

I want to start with a simple Task entity. Since an entity is part of the domain layer, I added it into the .Coreproject:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing;
using Abp.Timing;

namespace Acme.SimpleTaskApp.Tasks
{
    [Table("AppTasks")]
    public class Task : Entity, IHasCreationTime
    {
        public const int MaxTitleLength = 256;
        public const int MaxDescriptionLength = 64 * 1024; //64KB

        [Required]
        [MaxLength(MaxTitleLength)]
        public string Title { get; set; }

        [MaxLength(MaxDescriptionLength)]
        public string Description { get; set; }

        public DateTime CreationTime { get; set; }

        public TaskState State { get; set; }

        public Task()
        {
            CreationTime = Clock.Now;
            State = TaskState.Open;
        }

        public Task(string title, string description = null)
            : this()
        {
            Title = title;
            Description = description;
        }
    }

    public enum TaskState : byte
    {
        Open = 0,
        Completed = 1
    }
}


  • I derived from ABP's base Entity class, which includes Id property as int by default. We can use the generic version, Entity<TPrimaryKey>, to choice a different PK type.
  • IHasCreationTime is a simple interface just defines CreationTime property (it's good to use a standard name for CreationTime).
  • Task entity defines a required Title and an optional Description.
  • TaskState is a simple enum to define states of a Task.
  • Clock.Now returns DateTime.Now by default. But it provides an abstraction, so we can easily switch to DateTime.UtcNow in the feature if it's needed. Always use Clock.Now instead of DateTime.Now while working with ABP framework.
  • I wanted to store Task entities into AppTasks table in the database.

Adding Task to DbContext

.EntityFrameworkCore project includes a pre-defined DbContext. I should add a DbSet for the Task entity into the DbContext:

public class SimpleTaskAppDbContext : AbpDbContext
{
    public DbSet<Task> Tasks { get; set; }

    public SimpleTaskAppDbContext(DbContextOptions<SimpleTaskAppDbContext> options) 
        : base(options)
    {

    }
}


Now, EF Core knows that we have a Task entity. 

Creating the First Database Migration 

We will create an initial migration to create database and the AppTasks table. I open the Windows Command Line and run the following command (active directory should be the root directory of .EntityFrameworkCore project):

EF Core Initial Migration

This command creates a Migrations folder in the .EntityFrameworkCore project which includes a migration class and a snapshot of our database model:

EntityFrameworkCore Project Migrations 

Automatically generated "Initial" migration class is shown below:

public partial class Initial : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "AppTasks",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                CreationTime = table.Column<DateTime>(nullable: false),
                Description = table.Column<string>(maxLength: 65536, nullable: true),
                State = table.Column<byte>(nullable: false),
                Title = table.Column<string>(maxLength: 256, nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_AppTasks", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "AppTasks");
    }
}


This code is used to create AppTasks table when we execute the migrations to the database (see entity framework documentation for more information on migrations).

Creating the Database

To create the database, I run the following command in the command line:

dotnet ef database update

This command created a database named SimpleTaskAppDb in the local SQL Server and executed migrations (currently, there is a single, "Initial", migration):

Created Database

Now, I have a Task entity and corresponding table in the database.  I entered a few sample Tasks to the table:

AppTasks table

Note that, the database connection string is defined in appsettings.json in the .Web application.

Task Application Service

Application Services are used to expose domain logic to the presentation layer. An Application Service is called from presentation layer with a Data Transfer Object (DTO) as parameter (if needed), uses domain objects to perform some specific business logic and returns a DTO back to the presentation layer (if needed).

I'm creating the first application service, TaskAppService, into the .Application project to perform task related application logic. First, I wanted to define an interface for the app service:

public interface ITaskAppService : IApplicationService
{
    Task<ListResultOutput<TaskListDto>> GetAll(GetAllTasksInput input);
}

Defining an interface is not required, but suggested. By convention, all app services should implement IApplicationService interface in ABP. I just created a GetAll method to query tasks. To do that, I also defined the following DTOs:

public class GetAllTasksInput
{
    public TaskState? State { get; set; }
}

[AutoMapFrom(typeof(Task))] public class TaskListDto : EntityDto, IHasCreationTime { public string Title { get; set; } public string Description { get; set; } public DateTime CreationTime { get; set; } public TaskState State { get; set; } }
  • GetAllTasksInput DTO defines input parameters of the GetAll app service method. Instead of directly defining the state as method parameter, I added it into a DTO object. Thus, I can add other parameters into this DTO later without breaking my existing clients (we could directly add a state parameter to the method).
  • TaskListDto is used to return a Task data. It's derived from EntityDto, which just defines an Idproperty (we could add Id to our Dto and not derive from EntityDto). We defined [AutoMapFrom] attribute to create AutoMapper mapping from Task entity to TaskListDto. This attribute is defined in Abp.AutoMapper nuget package.
  • Lastly, ListResultOutput is a simple class contains a list of items (we could directly return a List<TaskListDto>).

Now, we can implement the ITaskAppService as shown below:

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Domain.Repositories;
using Abp.Linq.Extensions;
using Acme.SimpleTaskApp.Tasks.Dtos;
using Microsoft.EntityFrameworkCore;

namespace Acme.SimpleTaskApp.Tasks
{
    public class TaskAppService : SimpleTaskAppAppServiceBase, ITaskAppService
    {
        private readonly IRepository<Task> _taskRepository;

        public TaskAppService(IRepository<Task> taskRepository)
        {
            _taskRepository = taskRepository;
        }

        public async Task<ListResultOutput<TaskListDto>> GetAll(GetAllTasksInput input)
        {
            var tasks = await _taskRepository
                .GetAll()
                .WhereIf(input.State.HasValue, t => t.State == input.State.Value)
                .OrderByDescending(t => t.CreationTime)
                .ToListAsync();

            return new ListResultOutput<TaskListDto>(
                ObjectMapper.Map<List<TaskListDto>>(tasks)
            );
        }
    }
}


  • TaskAppService is derived from SimpleTaskAppAppServiceBase included in the startup template (which is derived from ABP's ApplicationService class). This is not required, app services can be plain classes. But ApplicationService base class has some pre-injected services (like ObjectMapper used here).
  • I used dependency injection to get a repository.
  • Repositories are used to abstract database operations for entities. ABP creates a pre-defined repository (like IRepository<Task> here) for each entity to perform common tasks. IRepository.GetAll()used here returns an IQueryable to query entities.
  • WhereIf is an extension method of ABP to simplify conditional usage of IQueryable.Where method.
  • ObjectMapper (which somes from the ApplicationService base class and implemented via AutoMapper by default) is used to map list of Task objects to list of TaskListDtos objects.

Testing the TaskAppService

Before going further to create user interface, I want to test TaskAppService. You can skip this section if you don't interest in automated testing.

Startup template contains .Tests project to test our code. It uses EF Core's InMemory database provider instead of SQL Server. Thus, our unit tests can work without a real database. It creates a separated database for each test. Thus, tests are isolated from each other. We can use TestDataBuilder class to add some initial test data to InMemory database before running tests. I changed TestDataBuilder as shown below:

public class TestDataBuilder
{
    private readonly SimpleTaskAppDbContext _context;

    public TestDataBuilder(SimpleTaskAppDbContext context)
    {
        _context = context;
    }

    public void Build()
    {
        _context.Tasks.AddRange(
            new Task("Follow the white rabbit", "Follow the white rabbit in order to know the reality."),
            new Task("Clean your room") { State = TaskState.Completed }
            );
    }
}


You can see the sample project's source code to understand where and how TestDataBuilder is used. I added two tasks (one of them is completed) to the dbcontext. So, I can write my tests assuming that there are two Tasks in the database. My first integration test tests the TaskAppService.GetAll method we created above.

public class TaskAppService_Tests : SimpleTaskAppTestBase
{
    private readonly ITaskAppService _taskAppService;

    public TaskAppService_Tests()
    {
        _taskAppService = Resolve<ITaskAppService>();
    }

    [Fact]
    public async System.Threading.Tasks.Task Should_Get_All_Tasks()
    {
        //Act
        var output = await _taskAppService.GetAll(new GetAllTasksInput());

        //Assert
        output.Items.Count.ShouldBe(2);
    }

    [Fact]
    public async System.Threading.Tasks.Task Should_Get_Filtered_Tasks()
    {
        //Act
        var output = await _taskAppService.GetAll(new GetAllTasksInput { State = TaskState.Open });

        //Assert
        output.Items.ShouldAllBe(t => t.State == TaskState.Open);
    }
}


I created two different tests to test GetAll method as shown above. Now, I can open Test Explorer (from Test\Windows\Test Explorer in the main menu of VS) and run the unit tests:

Test explorer

All of them succeed. The last one was a pre-built test in the startup template, which we can ignore for now.

Notice that; ABP startup template comes with xUnit and Shouldly installed by default. So, we used them to write our tests.

Task List View

Now, I know that TaskAppService is properly working. I can start to create a page to list all tasks.

Adding a New Menu Item

 First, I'm adding a new item to the top menu:

public class SimpleTaskAppNavigationProvider : NavigationProvider
{
    public override void SetNavigation(INavigationProviderContext context)
    {
        context.Manager.MainMenu
            .AddItem(
                new MenuItemDefinition(
                    "Home",
                    L("HomePage"),
                    url: "",
                    icon: "fa fa-home"
                    )
            ).AddItem(
                new MenuItemDefinition(
                    "About",
                    L("About"),
                    url: "Home/About",
                    icon: "fa fa-info"
                    )
            ).AddItem(
                new MenuItemDefinition(
                    "TaskList",
                    L("TaskList"),
                    url: "Tasks",
                    icon: "fa fa-tasks"
                    )
            );
    }

    private static ILocalizableString L(string name)
    {
        return new LocalizableString(name, SimpleTaskAppConsts.LocalizationSourceName);
    }
}


Startup template comes with two pages: Home and About, as shown above. We can change them or create new pages. I prefered to leave them for now and create a new menu item.

Creating the TaskController and ViewModel

I'm creating a new controller class, TasksController, in the .Web project as shown below:

public class TasksController : SimpleTaskAppControllerBase
{
    private readonly ITaskAppService _taskAppService;

    public TasksController(ITaskAppService taskAppService)
    {
        _taskAppService = taskAppService;
    }

    public async Task<ActionResult> Index(GetAllTasksInput input)
    {
        var output = await _taskAppService.GetAll(input);
        var model = new IndexViewModel(output.Items);
        return View(model);
    }
}


  • I derived from SimpleTaskAppControllerBase (which is derived from AbpController) that contains common base code for Controllers in this application.
  • I injected ITaskAppService in order to get list of tasks.
  • Instead of directly passing result of the GetAll method to the view, I created an IndexViewModel class in the .Web project which is shown below:
public class IndexViewModel
{
    public IReadOnlyList<TaskListDto> Tasks { get; }

    public IndexViewModel(IReadOnlyList<TaskListDto> tasks)
    {
        Tasks = tasks;
    }

    public string GetTaskLabel(TaskListDto task)
    {
        switch (task.State)
        {
            case TaskState.Open:
                return "label-success";
            default:
                return "label-default";
        }
    }
}


This simple view model gets a list of tasks (which is provided from ITaskAppService) in it's constructor. It also has GetTaskLabel method that will be used in the view to select a Bootstrap label class for given task.

Task List Page

And finally, the Index view is shown below:

@model Acme.SimpleTaskApp.Web.Models.Tasks.IndexViewModel

@{
    ViewBag.Title = L("TaskList");
    ViewBag.ActiveMenu = "TaskList"; //Matches with the menu name in SimpleTaskAppNavigationProvider to highlight the menu item
}

<h2>@L("TaskList")</h2>

<div class="row">
    <div>
        <ul class="list-group">
            @foreach (var task in Model.Tasks)
            {
                <li class="list-group-item">
                    <span class="pull-right label @Model.GetTaskLabel(task)">@L($"TaskState_{task.State}")</span>
                    <h4 class="list-group-item-heading">@task.Title</h4>
                    <div class="list-group-item-text">
                        @task.CreationTime.ToString("yyyy-MM-dd HH:mm:ss")
                    </div>
                </li>
            }
        </ul>
    </div>
</div>


We simply used given model to render the view using Bootstrap's list group component. Here, we used IndexViewModel.GetTaskLabel() method to get label types for tasks. Rendered page will be like that:

Task list

Localization

We used L method in the view which comes from ABP framework. It's used to localize strings. We have define localized strings in Localization/Source folder in the .Core project as .json files. English localization is shown below:

{
  "culture": "en",
  "texts": {
    "HelloWorld": "Hello World!",
    "ChangeLanguage": "Change language",
    "HomePage": "HomePage",
    "About": "About",
    "Home_Description": "Welcome to SimpleTaskApp...",
    "About_Description": "This is a simple startup template to use ASP.NET Core with ABP framework.",
    "TaskList": "Task List",
    "TaskState_Open": "Open",
    "TaskState_Completed": "Completed"
  }
}


Most of the texts are coming from startup template and can be deleted. I just added the last 3 lines and used in the view above. While using ABP's localization is pretty simple, you can see localization document for more information on the localization system.

Filtering Tasks

As shown above, TaskController actually gets a GetAllTasksInput that can be used to filter tasks. So, we can add a dropdown to task list view to filter tasks. First, I added the dropdown to the view (I added inside the header):

<h2>
    @L("TaskList")
    <span class="pull-right">
        @Html.DropDownListFor(
           model => model.SelectedTaskState,
           Model.GetTasksStateSelectListItems(LocalizationManager),
           new
           {
               @class = "form-control",
               id = "TaskStateCombobox"
           })
    </span>
</h2>


Then I changed IndexViewModel to add SelectedTaskState property and GetTasksStateSelectListItems method:

public class IndexViewModel
{
    //...

    public TaskState? SelectedTaskState { get; set; }

    public List<SelectListItem> GetTasksStateSelectListItems(ILocalizationManager localizationManager)
    {
        var list = new List<SelectListItem>
        {
            new SelectListItem
            {
                Text = localizationManager.GetString(SimpleTaskAppConsts.LocalizationSourceName, "AllTasks"),
                Value = "",
                Selected = SelectedTaskState == null
            }
        };

        list.AddRange(Enum.GetValues(typeof(TaskState))
                .Cast<TaskState>()
                .Select(state =>
                    new SelectListItem
                    {
                        Text = localizationManager.GetString(SimpleTaskAppConsts.LocalizationSourceName, $"TaskState_{state}"),
                        Value = state.ToString(),
                        Selected = state == SelectedTaskState
                    })
        );

        return list;
    }
}


We should set SelectedTaskState in the controller:

public async Task<ActionResult> Index(GetAllTasksInput input)
{
    var output = await _taskAppService.GetAll(input);
    var model = new IndexViewModel(output.Items)
    {
        SelectedTaskState = input.State
    };
    return View(model);
}


Now, we can run the application to see the combobox at the top right of the view:

Task list

I added the combobox but it can not work yet. I'll write a simple javascript code to re-request/refresh task list page when combobox value changes. So, I'm creating wwwroot\js\views\tasks\index.js file in the .Web project:

(function ($) {
    $(function () {

        var _$taskStateCombobox = $('#TaskStateCombobox');

        _$taskStateCombobox.change(function() {
            location.href = '/Tasks?state=' + _$taskStateCombobox.val();
        });

    });
})(jQuery);


Before including this javascript file into my view, I used Bundler & Minifier VS extension (which is default way of minifying files in ASP.NET Core projects) to minify the script:

Minify js

This adds the following lines into bundleconfig.json file in the .Web project:

{
  "outputFileName": "wwwroot/js/views/tasks/index.min.js",
  "inputFiles": [
    "wwwroot/js/views/tasks/index.js"
  ]
}


And creates a minified version of the script:

Minified js file

Whenever I change the index.js, index.min.js is automatically re-generated. Now, I can include the javascript file into my page:

@section scripts
{
    <environment names="Development">
        <script src="~/js/views/tasks/index.js"></script>
    </environment>

    <environment names="Staging,Production">
        <script src="~/js/views/tasks/index.min.js"></script>
    </environment>
}


With this code, our view will use index.js in development and index.min.js (minified version) in production. This is a common approach in ASP.NET Core MVC projects.

Automated Testing Task List Page

We can create integration tests that is also integrated to ASP.NET Core MVC infrastructure. Thus, we can completely test our server side code. You can skip this section if you don't interest in automated testing.

ABP startup template includes a .Web.Tests project to do that. I created a simple test to request to TaskController.Index and check the response:

public class TasksController_Tests : SimpleTaskAppWebTestBase
{
    [Fact]
    public async System.Threading.Tasks.Task Should_Get_Tasks_By_State()
    {
        //Act

        var response = await GetResponseAsStringAsync(
            GetUrl<TasksController>(nameof(TasksController.Index), new
                {
                    state = TaskState.Open
                }
            )
        );

        //Assert

        response.ShouldNotBeNullOrWhiteSpace();
    }
}


GetResponseAsStringAsync and GetUrl methods are some helper methods provided by AbpAspNetCoreIntegratedTestBase class of ABP. We can instead directly use the Client (an instance of HttpClient) property to make requests. But using these shortcut methods makes it easier. See integration testing documentation of ASP.NET Core for more.

When I debug the test, I can see the response HTML:

Web test

That shows the Index page returned a response without any exception. But... we may want to go more and check if returned HTML is what we expect. There are some libraries can be used to parse HTML. AngleSharp is one of them and comes as pre-installed in ABP startup template's .Web.Tests project. So, I used it to check the created HTML code:

public class TasksController_Tests : SimpleTaskAppWebTestBase
{
    [Fact]
    public async System.Threading.Tasks.Task Should_Get_Tasks_By_State()
    {
        //Act

        var response = await GetResponseAsStringAsync(
            GetUrl<TasksController>(nameof(TasksController.Index), new
                {
                    state = TaskState.Open
                }
            )
        );

        //Assert

        response.ShouldNotBeNullOrWhiteSpace();

        //Get tasks from database
        var tasksInDatabase = await UsingDbContextAsync(async dbContext =>
        {
            return await dbContext.Tasks
                .Where(t => t.State == TaskState.Open)
                .ToListAsync();
        });

        //Parse HTML response to check if tasks in the database are returned
        var document = new HtmlParser().Parse(response);
        var listItems = document.QuerySelectorAll("#TaskList li");
            
        //Check task count
        listItems.Length.ShouldBe(tasksInDatabase.Count);

        //Check if returned list items are same those in the database
        foreach (var listItem in listItems)
        {
            var header = listItem.QuerySelector(".list-group-item-heading");
            var taskTitle = header.InnerHtml.Trim();
            tasksInDatabase.Any(t => t.Title == taskTitle).ShouldBeTrue();
        }
    }
}


You can check the HTML deeper and in more detailed. But in most cases, checking the fundamental tags will be enough.

LINK: https://www.codeproject.com/articles/1115763/using-asp-net-core-entity-framework-core-and-asp-n

.NET Core Datagrid

.Net Core datagrid with server side paging, sorting and filtering

Showing data in grid format is an important task for many web applications. This blog gives a demo of how to display data with the Bootstrap Table plug in. The demo shows advanced features like server side paging, filtering and sorting.

With the demo application, I cover these aspects in more detail:

  1. Setup Bootstrap Table plug in
  2. Setup DataSource
  3. Table definition in cshtml file
  4. Custom cell rendering
  5. Server side paging, sorting and filtering
  6. Highlight selected row
  7. Custom Toolbar
  8. Additional Load parameters

Bootstrap Table plug in

There are many data grid packages available, all with their own strengths and weaknesses. In this demo, I use the Bootstrap Table plug in. It's a free plug in with useful features:

  1. Ajax enabled
  2. Server side paging, filtering and sorting
  3. Easy to use
  4. Lightweight and fast
  5. Bootstrap support
  6. Third party packages are available for extra functionality

Setup Bootstrap Table plug in

Start Visual Studio and create a new .NET Core Project with no authentication.

Bootstrap Table is a JavaScript library and you only need to include the library files in the application. It has two files, a JavaScript and a CSS file. The rendersection is set at the end of the _Layout.cshtml file. The rendersection provides a hook to an individual cshtml page. This hook is executed during the rendering of the individual cshtml page.

  ...
  @RenderSection("scripts", required: false)
</body>
</html>
...
@section scripts { 
  @await Html.PartialAsync("bootstraptable")
 
  <script type="text/javascript">
  ...

This chain renders the shared bootstraptable.cshtml, located in the shared views folder. This makes re-use in other pages easy and the files are just in time loaded for optimal performance. Dot Net Core offers a neat solution to differentiate between production and development files. In development, you can use the larger, easy readable files, while for production, you automatically switch to the smaller and faster, minified files.

<environment names="Development">
  <link rel="stylesheet" href="~/css/bootstrap-table.css">
  <script src="~/lib/bootstrap-table/bootstrap-table.js"></script>
</environment>
<environment names="Staging,Production">
  <link rel="stylesheet" 
   href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.11.0/bootstrap-table.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.11.0/bootstrap-table.min.js">
  </script>
</environment>


Setup DataSource

In most cases, the data is retrieved from database server or a REST-api. This demo focuses on how to show data and less on how to get data. In this case, the data is fetched from a simple JSON file, located at the web server. This keeps the datasource simple and easy to setup. The controller has a private IList<Country> countries property. Country is a simple POCO class:

public class Country
{
  [Key()]
  public Int32 Code { get; set; }

  [StringLength(2, MinimumLength =2)]
  [Display(Name="2 Digit ISO")]
  public String ISO2{ get; set; }

  [StringLength(3, MinimumLength = 3)]
  [Display(Name = "3 Digit ISO")]
  public String ISO3 { get; set; }

  public String Name { get; set; }
}
 

Entity Framework Support

This sample uses IList<Country> as datasource, but it can easily be replaced by a DbSet<Country> delivered by the Entity Framework. The demo works with every kind of datasource, provided the datasource can be cast to the none generic IQueryable type.

Table Definition in cshtml File

You can setup the table definition in either HTML or jquery. I prefer HTML because I find it easier to read. A combination is also possible. In this demo, HTML sets the table layout and the table events are handled in jquery. Data attributes in the <table> section sets the table behavior, the columns and rows are configured in the <tr> section.

<table id="table"
         data-unique-id="Code"
         data-sort-name="Code"
         data-sort-order="asc"
         data-classes="table table-condensed table-hover table-striped"
         data-toggle="table"
         data-side-pagination="server"
         data-url="Load"
         data-pagination="true"
         data-search="true"
         data-show-refresh="true"
         data-toolbar="#toolbar"
         data-page-size="20"
         data-page-list="[5,10,20,50,100,All]">
    <thead>
      <tr>
        <th data-field="ISO2" data-sortable="false" 
        data-halign="center" data-align="center" 
         data-formatter="flagFormatter">Flag</th>
        <th data-field="Code" data-sortable="true" 
        data-halign="center" data-align="center">Code</th>
        <th data-field="ISO3" data-sortable="true">ISO 3</th>
        <th data-field="Name" data-sortable="true">Name</th>
      </tr>
    </thead>
  </table>


Column settings like alignment, width, date and number formats can be configured with attributes. The data-unique-id="Code" sets the primary key column. During rendering, each table row gets a data-uniqueidattribute with its key value.

<tr data-uniqueid="4" data-index="0">
  ...
</tr>

This key attribute is vital for CRUD (Create, Retrieve, Update and Delete) operations. With jquery, you can retrieve the key value from a row with little effort. If the data-unique-id is empty or not set, the table will still be rendered. The data-sort-name and data-sort-order attributes set the initial sort column and order. These values are passed to the controller during the data request.

Custom Cell Rendering

Bootstrap Table supports custom cell rendering with the data-formatter attribute. This offers maximum flexibility if the standard configuration options are not enough.

function flagFormatter(value, row) {
     return '<img src="/images/flags/' + value.toLowerCase() + '.png" >';
   }
 

The value parameter is the column value and row parameter contains all the row values.

Server Side Paging, Sorting and Filtering

It takes only a few attributes to setup server side paging, sorting and filtering.

data-side-pagination="server"
data-url="Load"
data-pagination="true"
data-search="true"

The data-url="Load" attribute specifies that the controller class has a public Load function. The Loadfunction handles the input parameters and returns a JSON document.

[HttpGet]
public virtual ActionResult Load(String sort, String order, String search, Int32 limit, Int32 offset)
{
   // Get entity fieldnames
   List<String> columnNames = typeof(Country).GetProperties(BindingFlags.Public | 
                              BindingFlags.Instance).Select(p => p.Name).ToList();

   // Create a separate list for searchable field names   
   List<String> searchFields = new List<String>(columnNames);

   // Exclude field Iso2 for filtering 
   searchFields.Remove("ISO2");

   // Perform filtering
   IQueryable items = SearchItems(countries.AsQueryable(), search, searchFields);

   // Sort the filtered items and apply paging
   return Content(ItemsToJson
   (items, columnNames, sort, order, limit, offset), "application/json");
}


Input parameters:

  • sort: sort column name
  • order: sort direction, asc or desc
  • search: search argument entered by user
  • limit: page size
  • offset: number of records to skip before fetching the data page

JSON Output:

  • total: number of records available after filtering
  • rows: array with country records

The array capacity is equal to or less than the limit page size.

"total": 193,
"rows": [
  {
    "Code": 4,
    "ISO2": "AF",
    "ISO3": "AFG",
    "Name": "Afghanistan"
  },
  {
    "Code": 8,
    "ISO2": "AL",
    "ISO3": "ALB",
    "Name": "Albania"
  },
 ...
 

Exclude Filter Fields

Field ISO2 is used for rendering the flag image, the code itself is not visible for the user. In this GUI design, the search parameter applies only for visible data. This means that the ISO2 property must be excluded from searchable fields.

// Get entity fieldnames
List<String> columnNames = typeof(Country).GetProperties(BindingFlags.Public | 
                           BindingFlags.Instance).Select(p => p.Name).ToList();

// Create a separate list for searchable field names   
List<String> searchFields = new List<String>(columnNames);

// Exclude field Iso2 for filtering 
searchFields.Remove("ISO2");

// Perform filtering
IQueryable items = SearchItems(countries.AsQueryable(), search, searchFields);


Reusable Filtering with Dynamic Linq

Linq is a great innovation. It gives the ability to execute queries on an enumerable collection. Unlike SQL, Linq has compile time syntax checking. This is helpful in most cases. It already detects errors during compile time instead of runtime. If I want to create a reusable filter method, the compile time syntax becomes an obstacle. Different entities has different fieldnames, so how to pass different field names to one reusable method? Runtime parsing instead of compile time parsing is the solution. The Dynamic Linq Core packages does just this. It smoothly integrates with Linq and provides extra overload functions for where clauses, sorting and other operations. Dynamic Linq is used in SearchItems to create a searchExpression at runtime.

protected virtual IQueryable SearchItems(IQueryable items, String search, List<String> columnNames)
{
  // Apply filtering to all visible column names
  if (search != null && search.Length > 0)
  {
    StringBuilder sb = new StringBuilder();

    // create dynamic Linq expression
    foreach (String fieldName in columnNames)
      sb.AppendFormat("({0} == null ? 
      false : {0}.ToString().IndexOf(@0, @1) >=0) or {1}", 
                                      fieldName, Environment.NewLine);

    String searchExpression = sb.ToString();
    // remove last "or" occurrence
    searchExpression = searchExpression.Substring
    (0, searchExpression.LastIndexOf("or"));

    // Apply filtering, 
    items = items.Where
    (searchExpression, search, StringComparison.OrdinalIgnoreCase);
  }
  return items;
}


Country searchExpression generated by SearchItems:

(Code == null ? false : Code.ToString().IndexOf(@0, @1) >=0) or 
(ISO3 == null ? false : ISO3.ToString().IndexOf(@0, @1) >=0) or 
(Name == null ? false : Name.ToString().IndexOf(@0, @1) >=0)

Please note the ISO2 field is not in the searchExpression as expected. In this demo, the SearchItemsimplementation is pretty straightforward. If a more complicated filtering is required, the SearchItems can be overridden to meet the new needs.

Generating JSON Document

The ItemsToJson function creates the JSON document that is consumed by the Bootstrap Table.

protected String ItemsToJson(IQueryable items, List<String> columnNames, 
                             String sort, String order, Int32 limit, Int32 offset)
{
  try
  {
	// where clause is set, count total records
	Int32 count = items.Count();

	// Skip requires sorting, so make sure there is always sorting
	String sortExpression = "";
   
	if (sort != null && sort.Length > 0)
	  sortExpression += String.Format("{0} {1}", sort, order);

	// show all records if limit is not set
	if (limit == 0)
	  limit = count;

	// Prepare json structure
	var result = new
	{
	  total = count,
	  rows = items.OrderBy(sortExpression).Skip(offset).Take(limit).Select
             ("new (" + String.Join(",", columnNames) + ")")
	};

	return JsonConvert.SerializeObject(result, Formatting.None, new JsonSerializerSettings() 
                  { MetadataPropertyHandling = MetadataPropertyHandling.Ignore });
  }
  catch (Exception ex)
  {
	Console.WriteLine(ex.Message);
	return null;
  }
}


Input parameters:

  • items: unsorted, filtered entity set
  • columnNames: fields included in JSON document
  • sort: sort column name
  • order: sort direction, asc or desc
  • search: search argument entered by user
  • limit: page size
  • offset: number of records to skip before fetching the data page

The columnNames variable limits the number of properties exposed in the JSON document. This can be useful if you don't want to show all available entities properties for performance or security reasons. The paging requires sorting and is provided by Dynamic Linq. The paging is implemented with the standard Linq Skip and Takefunctions. The option Formatting.None reduces the JSON document size, and increases performance, but makes it more difficult to read. I only use the option Formatting.Indented for debugging purposes.

Highlight Selected Row

Bootstrap Table has several row selection options. It can even remember selected rows on a previous page, which I think is pretty cool. You can read the documentation on how this can be done. In the demo application, I use jquery and CSS. On forums, there are many questions about this topic, so let's give it some attention here. First, I modified the CSS style to make the selected row more apparent. I could overwrite the bootstrap CSS file, but all the work would be lost in case of an update. Setting the new style in the site.css file frees you from this risk.

/* selected row style */
.table-striped tbody .highlight td {
  background-color: #b5b5b5;
}

The next step is to attach the Bootstrap Table row click event to the highLightRow function.

// register row-click event
$('#table').on('click-row.bs.table', function ($element, row, $tr) {
  highLightRow($tr);
});


The highLightRow function assigns the highlight CSS class to the selected row and removes the CSS class from all other rows. This makes sure that only one row at a time is selected.

function highLightRow($tr) {
   $tr.addClass('highlight').siblings().removeClass('highlight');
}


Custom Toolbar

With Bootstrap Table, you can customize the toolbar with just plain HTML. In some other packages, you need to know a lot about the grid details and its API. Bootstrap Table is breathtakingly simple. Create your toolbar buttons and other controls inside a div with an id. Assign this id to the data-toolbar attribute and that's all it takes!

<div id="toolbar">
  <button id="btninfo" title="Show selected row info" class="btn btn-default" type="button">
  <i class="glyphicon glyphicon-info-sign"></i> row info</button>
</div>>
 
<table id="table"
 ...
 data-toolbar="#toolbar"
 ...

Additional Load Parameters

Sometimes, the GUI requires that additional parameters are sent to the controller. This takes only a few simple steps. First, set what function injects the extra parameter with the data-query-params attribute.

<table id="table"
 ...
 data-query-params="extraServerParams"
 ...

In this demo, the extra parameter is fixed, normally, you would use the value of an input control.

function extraServerParams(params) {
   params.ExtraParam = 2;
   return params;
}

The last step is modifying the Load function on the controller to process the extra parameter.

Conclusion

Displaying data in a grid is an important requirement for many applications. Bootstrap Table does an excellent job at this. It's easy to setup and use and works well with Bootstrap and Dot Net Core. Dynamic Linq makes the solution highly reusable. I added the demo application so you can play with it. If you have any comments or questions, please let me know.

Further Reading

LINK: https://www.codeproject.com/Articles/1166225/NET-Core-Datagrid

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