TRUNGTQ

Think Big, Act Small, Fail Fast and Learn Rapidly

NAVIGATION - SEARCH

CRUD USING .NET CORE 1.0, ANGULARJS2, WEBAPI

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

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

In this article we are going to explore,

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

Let’s get started with step by step:

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

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

USE [PhoneBook]
GO

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

SET QUOTED_IDENTIFIER ON
GO

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

GO
 

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

core_crud_1

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

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

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

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

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

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

core_crud_4

Save changes after modification.

core_crud_5

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

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

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

For Package Manager Console:

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

For Command Window:

  • dotnet ef dbcontext scaffold

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

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

core_crud_6

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

core_crud_7

Input below command then hit enter,

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

 core_crud_8core_crud_9

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

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

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

core_crud_10

NextInput below command then hit enter,

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

core_crud_11

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

core_crud_12

Configure EF Service: In PhoneBookContext Class add constructor

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

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

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

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

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

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

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

API Controller

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

core_crud_14

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

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

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

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

GET

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

POST

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

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

PUT

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

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

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

DELETE

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

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

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

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

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

Startup.cs

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

need to add library

using Microsoft.AspNetCore.Builder;

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

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

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

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

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

Index.html

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

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

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

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

  Bootstrap, Model, Component & Route Main.ts

/*This is the spa bootstrap File*/

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

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

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

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

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

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

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


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


  Home.ts

import {Component} from 'angular2/core';

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

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


Contact.component.ts

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

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

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

  Contact.main.ts

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Contact.service.ts

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

@Component({
    providers: [Http]
})

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

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

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

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

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

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

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

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

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

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

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

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

  1. Template-driven
  2. Model-driven

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

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

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

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

ngFormModel:binding it to a controller variable “contactForm”

<form [ngFormModel]="contactForm">

ngFormControl

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

ControlGroup is contain of several Controls.

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

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

private builder: FormBuilder

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

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

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

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

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

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

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

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

Contact.view.html

<div class="row">

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

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

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

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

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

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

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

Make sure the dependencies is exist

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

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

core_crud_18

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

core_crud_19

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

core_crud_20

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

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

core_crud_21

Hope this will help :)

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

 

.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

Upgraded from project.json to csproj for SimplCommerce

Upgraded from project.json to csproj for SimplCommerce – Thien Nguyen – a developer

Today, we have upgraded from project.json to csproj for SimplCommerce. It’s simple than what I have thought. In this blog post I will share you this journey.

While working with ASP.NET Core in SimplCommerce, I felt in love with project.json. It’s an innovation, modern and simple. However, in order to make the tooling compatible with other .NET app models (WinForms, WPF, UWP, ASP.NET, iOS, Android, etc.), Microsoft has decided to move .NET Core projects to .csproj/MSBuild. More detail about this can be found here.

Struggling for a while, we finally decided to say goodbye to project.json.

Goodbye project.json

Microsoft doesn’t support .NET Core csproj on VS 2015. So, to work with csproj, we need to install VS 2017 RC or go with command line. I chose to install VS 2017 RC :). I use the lasted version of VS 2017 RC which included .NET Core 1.0 SDK – RC4.

After installed VS 2017 RC. I opened the SimplCommerce.sln. VS 2017 show a dialog and ask for an upgrade. Note it’s an one way upgrade. Once upgraded, you cannot go back.

Goodbye project.json

Took a deep breath and click OK. Whoo! sucessful, the migration report show no error, there was a warning on the solution file but it not important. All the projects are loaded.

Wait “Error occurred while restoring NuGet packages: The operation failed as details for project SimplCommerce.WebHost could not be loaded.” appeared in the Output panel. None of the project can build because all the dependencies could not be loaded.

Right click on the solution and then click “Restore Nuget Package” didn’t work aslo the same error appeared. Tried to workaroud in VS 2017, no luck.

Then I opened command line and typed “dotnet restore”, enter. wow

“C:\Program Files\dotnet\sdk\1.0.0-rc4-004771\NuGet.targets(97,5): error : ‘1.1.0-preview4-final;1.0.0-msbuild3-final’ is not a valid version string. [D:\Projects\SimplCommerce\SimplCommerce.sln]”

Let find this string. It is in the SimplCommerce.WebHost.csproj

<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.1.0-preview4-final;1.0.0-msbuild3-final" />

It seem not correct, let remove the “1.1.0-preview4-final;”

All the projects build sucessful. Control + F5 the application run just fine.

Let take a look at what have changed.

  • All the file project.json, **.xproj was removed. The global.json is removed also.
  • The files project.lock.json are renamed and moved here obj\project.assets.json
  • The SimplCommerce.sln just change a little but and seem not important
  • The **csproj look simple and nice

So SimplCommerce has been upgraded to use csproj. There are a couple of things I want to note here:

AppVeyor hasn’t offically support .NET Core csproj. But you can request to join their beta here Travis hasn’t support also. I have submited the request https://github.com/travis-ci/travis-ci/issues/7301

And below are some useful references:

 

 

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

Introduction

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

The code illustrates the following topics:

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

Getting Started

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

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

Running the Sample

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

Figure 1: Role listing

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

Figure 2: Add Application Role

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

Figure 3: Delete Application Role

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

Figure 4: Application User Listing

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

Figure 5: Add Application User

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

Figure 6: Edit Application User

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

Figure 7: Delete Application User

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

Figure 8: User Login Screen

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

Figure 9: Welcome Screen After Authorisation

If authenticate user is not authorised then shown following screen.

Figure 10: UnAuthorised Screen

Source Code Overview

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

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

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

Onion Architecture In ASP.NET Core MVC

Bài viết khá hay về ASP.NET Core, có thể tham khảo về kiến trúc

Introduction

A Visual Studio 2015 project which shows how to perform the create, read, update and delete operations in the ASP.NET Core MVC application using onion architecture with Entity Framework Core Code First approach.

The code illustrates the following topics:

  1. Listings, create, update and delete operations.
  2. One to one relationship in Entity Framework Core.
  3. Create, Update and Delete operations perform in bootstrap modal pop up with tag helpers.
  4. Database design using entity framework core code first approach with fluent API.
  5. Entity classes and repository in separate class library project and use migration to create database from it.
  6. Dependency injection for DbContext, Repository and service.

Article: Onion Architecture In ASP.NET Core MVC

Getting Started

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

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

Running the Sample

To run the sample, hit F5 or choose the Debug | Start Debugging menu command. You will see the user list screen. From this screen you have user listing screen as shown in below figure.

Figure 1: User listing

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

Figure 2: Add User

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

Figure 3: Edit User

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

Figure 4: Delete User

Source Code Overview

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

  1. wwwroot: It holds static js and css files.
  2. OA.Data: It’s separate class library project. It holds Entities and table configuration classes which create database.
  3. OA.Repo: It’s a class libraray project which holds DataContext and Repository.
  4. OA.Service: It’s a class libraray project which holds service interface which communicates to UI.
  5. appsettings.json:It holds database connection string.
  6. Migrations: It holds database migration files.

LINK: https://code.msdn.microsoft.com/Onion-Architecture-In-9c58c06d#content