15. ASP.NET Core 5 – CRUD Using Blazor And Entity Framework Core

Ankit Sharma, 2020.05.28 Updated to .NET Core 3.2 Preview-1
Tutorial: https://www.c-sharpcorner.com/article/asp-net-core-crud-using-blazor-and-entity-framework-core/

Github: https://github.com/AnkitSharma-007/ASPCore.BlazorCrud

Deploy on IIS : https://www.c-sharpcorner.com/article/deploying-a-blazor-application-on-iis/

 

Employee CRUD module (app.)

(Record Management System)

Open MS SQL Server Management studio (tool simmilar to Oracle SQLDeveloper or PHPMyadmin) and create tblEmployee table (catalog) in eg WizLib DB. Connect params are :
Server type:     DB engine
Server name :  localhost\SQLEXPRESS
Auth : Win auth
User name :  SSPC2/ss

Create table tblEmployee( 
  EmployeeId int IDENTITY(1,1) NOT NULL, 
  Name varchar(20) NOT NULL, 
  City varchar(20) NOT NULL, 
  Department varchar(20) NOT NULL, 
  Gender varchar(6) NOT NULL 
)

STEP 1. same as in Calculator example (L:\3_sw_video\net_core\BlazorDemoCalc_AnkitSharma202005.html).

Be aware: Sharma’s tutorials are older than his Github code (.sln) which is in this article !

3 project files (in corresponding 3 dirs) created inside solution dir BlazorCrudEMP : see J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud.sln

  1. BlazorCrud.Client – It has the client-side code and contains the pages that will be rendered on the browser.
  2. BlazorCrud.Server – It has the server-side codes such as DB related operations and web API.
  3. BlazorCrud.Shared – It contains the shared code that can be accessed by both client and server.

Execute the program – click F5 or on IIS while on client proj. – displays navig.menu links Home and Fetch Employees.
No more M, V and C dirs !

Original Counter and Fetch Data pages created automaticaly we deleted.

 

STEP 2. Adding Model to App

  1. Right-click on BlazorCrud.Shared project and then select Add >> New Folder and name the folder as Models. We will be adding our model class in this folder only.
  2. Right-click on Models folder and select Add >> Class. Name your class Employee. This class will contain our Employee model properties.
  3. Open Employee.cs
    J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Shared\Models\Employee.cs and put the following code in it :
// J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Shared\Models\Employee.cs
using System.ComponentModel.DataAnnotations;

namespace BlazorCrud.Shared.Models
{
    public class Employee
    {
        public int EmployeeId { get; set; }
        [Required]
        public string Name { get; set; }
        [Required]
        public string Gender { get; set; }
        [Required]
        public string Department { get; set; }
        [Required]
        public string City { get; set; }
    }
}

 

STEP 3. Creating DAL for App

DAL is Data Access Layer, here connect string and adapter CRUD functions for calling CRUD fns of lower level.

Right click on Models folder in BlazorCrud.Shared project  and select Add >> Class. Name your class EmployeeContext. This is our Entity Framework DB context class to interact with database. Open EmployeeContext.cs and put the following code into it.

using BlazorCrud.Shared.Models;
// J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Shared\Models\EmployeeContext.cs
using Microsoft.EntityFrameworkCore;

namespace BlazorCrud.Server.DataAccess
{
    public class EmployeeContext : DbContext
    {
        public virtual DbSet<Employee> tblEmployee { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                //"ora7": "User Id=hr;Password=hr; Data Source=sspc2:1521/XE;"
                //optionsBuilder.UseSqlServer(@"Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=myTestDB;Data Source=ANKIT-LENOVO\SQLEXPRESS;");
                optionsBuilder.UseSqlServer(@"Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=WizLib;Data Source=localhost\SQLEXPRESS;");
            }
        }
    }
}

Do not forget to put your own connection string.

Right click on BlazorCrud.Server project and then select Add >> New Folder and name the folder DataAccess. We will be adding our classes to handle database related operations inside this folder only. Add class to DataAccess folder and name it as EmployeeDataAccessLayer.

// J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Server\DataAccess\EmployeeDataAccessLayer.cs
// This class will handle our CRUD related DB operations
using BlazorCrud.Server.Interfaces;
using BlazorCrud.Shared.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BlazorCrud.Server.DataAccess
{
    public class EmployeeDataAccessLayer: IEmployee
    {
        EmployeeContext db = new EmployeeContext();

        //To Get all employees details   
        public IEnumerable<Employee> GetAllEmployees()
        {
            try
            {
                return db.tblEmployee.ToList();
            }
            catch
            {
                throw;
            }
        }

        //To Add new employee record     
        public void AddEmployee(Employee employee)
        {
            try
            {
                db.tblEmployee.Add(employee);
                db.SaveChanges();
            }
            catch
            {
                throw;
            }
        }

        //To Update the records of a particluar employee    
        public void UpdateEmployee(Employee employee)
        {
            try
            {
                db.Entry(employee).State = EntityState.Modified;
                db.SaveChanges();
            }
            catch
            {
                throw;
            }
        }

        //Get the details of a particular employee    
        public Employee GetEmployeeData(int id)
        {
            try
            {
                Employee employee = db.tblEmployee.Find(id);
                return employee;
            }
            catch
            {
                throw;
            }
        }

        //To Delete the record of a particular employee    
        public void DeleteEmployee(int id)
        {
            try
            {
                Employee emp = db.tblEmployee.Find(id);
                db.tblEmployee.Remove(emp);
                db.SaveChanges();
            }
            catch
            {
                throw;
            }
        }
    }
}

And hence our data access layer is complete.

 

 

Now, we will proceed to create our web API Controller.

STEP 4. Adding web API Controller to App

Right click on BlazorCrud.Server/Controllers folder and select Add >> New Item. An “Add New Item” dialog box will open. Select ASP.NET from the left panel, then select “API Controller Class” from templates panel and put the name as EmployeeController. Press OK.

EmployeeController class calls methods of EmployeeDataAccessLayer class and pass on data to client side.

// J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Server\Controllers\EmployeeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BlazorCrud.Server.Interfaces;
using BlazorCrud.Shared.Models;
using Microsoft.AspNetCore.Mvc;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace BlazorCrud.Server.Controllers
{
    [Route("api/[controller]")]
    public class EmployeeController : Controller
    {
        private readonly IEmployee objemployee;

        public EmployeeController(IEmployee _objemployee)
        {
            objemployee = _objemployee;
        }

        [HttpGet]
        [Route("Index")]
        public IEnumerable<Employee> Index()
        {
            return objemployee.GetAllEmployees();
        }

        [HttpPost]
        [Route("Create")]
        public void Create([FromBody] Employee employee)
        {
            if (ModelState.IsValid)
                objemployee.AddEmployee(employee);
        }

        [HttpGet]
        [Route("Details/{id}")]
        public Employee Details(int id)
        {

            return objemployee.GetEmployeeData(id);
        }

        [HttpPut]
        [Route("Edit")]
        public void Edit([FromBody]Employee employee)
        {
            if (ModelState.IsValid)
                objemployee.UpdateEmployee(employee);
        }

        [HttpDelete]
        [Route("Delete/{id}")]
        public void Delete(int id)
        {
            objemployee.DeleteEmployee(id);
        }
    }
}

We are done with our backend logic. So, we will now proceed to code our client side.

 

STEP 5. Adding Razor Views to App

Right click on BlazorCrud.Client/Pages folder and then select Add >> New Item. An “Add New Item” dialog box will open, select Web from the left panel, then select “Razor View” from templates panel and name it FetchEmployee.cshtml.

This will add a FetchEmployee.cshtml page to our BlazorCrud.Client/Pages folder. Similarly add 3 more pages AddEmployee.cshtml, EditEmployee.cshtml and DeleteEmployee.cshtml.

Let’s add codes to these pages

5.1 V FetchEmployee.razor (was .cshtml)

This page will be displaying all the employee records present in the database. Additionally, we will also provide action methods Edit and Delete on each record. Open FetchEmployee.cshtml and put the following code in it.

@* J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Client\Pages\FetchEmployee.razor *@
@page "/fetchemployee"
@using BlazorCrud.Shared.Models
@inject HttpClient Http


<h1>Employee Data</h1>
<p>This component demonstrates fetching Employee data from the server.</p>

<p>
    <a href="/addemployee">Create New</a>
</p>

@if (empList == null)
{
<p><em>Loading...</em></p> }
            else
            {
<table class='table'>
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Gender</th>
            <th>Department</th>
            <th>City</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var emp in empList)
        {
<tr>
    <td>@emp.EmployeeId</td>
    <td>@emp.Name</td>
    <td>@emp.Gender</td>
    <td>@emp.Department</td>
    <td>@emp.City</td>
    <td>
        <a href='/editemployee/@emp.EmployeeId'>Edit</a>  |
        <a href='/delete/@emp.EmployeeId'>Delete</a>
    </td>
</tr>}
    </tbody>
</table>}


@code { Employee[] empList;

  protected override async Task OnInitializedAsync()
  {
     empList = await Http.GetJsonAsync<Employee[]>("/api/Employee/Index");
  }
}

Let’s understand this code

On the top, we have included Blazor.Shared.Models namespace so that we can use our Employee model class in this page.

We are defining the route of this page using @page directive. So, in this application, if we append “/fetchemployee” to base URL then we will be redirected to this page.

We are also injecting HttpClient service to enable web API call.

Then we have defined the HTML part to display all the employees record in a tabular manner. We have also added two action links for Edit and Delete which will navigate to EditEmployee.cshtml and DeleteEmployee.cshtml pages.

At the bottom of the page, we have a @code (eas functions) section which contains our business logic. We have created an array variable empList of type Employee and populating it inside OnInitAsync method by calling our web API. This will bind to our HTML table on the page load.

 

5.2 V AddEmployee.razor

This page is used to create a new employee record.

@* J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Client\Pages\AddEmployee.razor *@
@page "/addemployee"
@using BlazorCrud.Shared.Models
@inject HttpClient Http
@inject NavigationManager urlNavigationManager

<h1>Create Employee</h1>
<hr />

<EditForm Model="@emp" OnValidSubmit="CreateEmployee">
    <DataAnnotationsValidator />
    <div class="form-group row">
        <label class="control-label col-md-12">Name</label>
        <div class="col-md-4">
            <input class="form-control" @bind="emp.Name" />
        </div>
        <ValidationMessage For="@(() => emp.Name)" />
    </div>
    <div class="form-group row">
        <label class="control-label col-md-12">Gender</label>
        <div class="col-md-4">
            <select class="form-control" @bind="emp.Gender">
                <option value="">-- Select Gender --</option>
                <option value="Male">Male</option>
                <option value="Female">Female</option>
            </select>
        </div>
        <ValidationMessage For="@(() => emp.Gender)" />
    </div>
    <div class="form-group row">
        <label class="control-label col-md-12">Department</label>
        <div class="col-md-4">
            <input class="form-control" @bind="emp.Department" />
        </div>
        <ValidationMessage For="@(() => emp.Department)" />
    </div>
    <div class="form-group row">
        <label class="control-label col-md-12">City</label>
        <div class="col-md-4">
            <input class="form-control" @bind="emp.City" />
        </div>
        <ValidationMessage For="@(() => emp.City)" />
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Save</button>
        <button class="btn btn-light" @onclick="Cancel">Cancel</button>
    </div>
</EditForm>

@code { Employee emp = new Employee();

   protected async Task CreateEmployee()
   {
      await Http.SendJsonAsync(HttpMethod.Post, "/api/Employee/Create", emp);
      urlNavigationManager.NavigateTo("/fetchemployee");
   }

   void Cancel()
   {
      urlNavigationManager.NavigateTo("/fetchemployee");
   }
 }

In this page the route is “/addemployee”.

We are also injecting urlNavigationManager (was “Microsoft.AspNetCore.Blazor.Services.IUriHelper”) service to enable URL redirection. The HTML part will generate a form to get inputs from the user. The attribute “bind” is used to bind the value entered in the textbox to the properties of Employee object.

In the @code (was functions) section we have defined two methods :

  1.  CreateEmployee will be invoked on clicking “Submit” button and send a POST request to our API along with the Employee object emp.
  2. Cancel method will be invoked on clicking cancel button and redirect the user back to FetchEmployee page.

5.3 V EditEmployee.razor

@* J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Client\Pages\EditEmployee.razor *@
@page "/editemployee/{empID:int}"
@using BlazorCrud.Shared.Models
@inject HttpClient Http
@inject NavigationManager urlNavigationManager

<h2>Edit Employee</h2>
<hr />

<EditForm Model="@emp" OnValidSubmit="UpdateEmployee">
    <DataAnnotationsValidator />
    <div class="form-group row">
        <label class="control-label col-md-12">Name</label>
        <div class="col-md-4">
            <input class="form-control" @bind="emp.Name" />
        </div>
        <ValidationMessage For="@(() => emp.Name)" />
    </div>
    <div class="form-group row">
        <label class="control-label col-md-12">Gender</label>
        <div class="col-md-4">
            <select asp-for="Gender" class="form-control" @bind="emp.Gender">
                <option value="">-- Select Gender --</option>
                <option value="Male">Male</option>
                <option value="Female">Female</option>
            </select>
        </div>
        <span><ValidationMessage For="@(() => emp.Gender)" /></span>
    </div>
    <div class="form-group row">
        <label class="control-label col-md-12">Department</label>
        <div class="col-md-4">
            <input class="form-control" @bind="emp.Department" />
        </div>
        <span><ValidationMessage For="@(() => emp.Department)" /></span>
    </div>
    <div class="form-group row">
        <label class="control-label col-md-12">City</label>
        <div class="col-md-4">
            <input class="form-control" @bind="emp.City" />
        </div>
        <span><ValidationMessage For="@(() => emp.City)" /></span>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Save</button>
        <button class="btn btn-light" @onclick="Cancel">Cancel</button>
    </div>
</EditForm>

@code { [Parameter]
    public int empID { get; set; }

    Employee emp = new Employee();

    protected override async Task OnInitializedAsync()
    {
        emp = await Http.GetJsonAsync<Employee>("/api/Employee/Details/" + empID);
    }

    protected async Task UpdateEmployee()
    {
        await Http.SendJsonAsync(HttpMethod.Put, "api/Employee/Edit", emp);
        urlNavigationManager.NavigateTo("/fetchemployee");
    }

    void Cancel()
    {
        urlNavigationManager.NavigateTo("/fetchemployee");
    }
}

In this page we have defined the route as “/editemployee/{empID}”. empID is an URL parameter of type string declared in @code section. We will use the [Parameter] attribute to mark the variable as a parameter. To navigate to this page, we need to pass the employee id in the URL which will be captured in empID variable. If we do not mark the variable with the [Parameter] attribute, we will get an error “Object of type ‘BlazorCrud.Client.Pages.EditEmployee’ has a property matching the name ’empID’, but it does not have [ParameterAttribute] applied.”. This will not allow empID to bind to the employee id value passed in the parameter.

The HTML part is similar to that of AddEmployee.cshtml page. The attribute “bind” is used for two-way binding; i.e., binding edited textbox values to employee object properties and vice versa.

Inside the @code section we are fetching the employee records in OnInitAsync method based on the employeeID passed in the parameter. This will bind to the fields in the form on page load itself.

UpdateEmployee method will send a PUT request to our API along with the Employee object emp. The Cancel method will be invoked on clicking cancel button and redirect the user back to FetchEmployee page.

5.4 V DeleteEmployee.razor

@* J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Client\Pages\DeleteEmployee.razor  *@
@page "/delete/{empID:int}"
@using BlazorCrud.Shared.Models
@inject HttpClient Http
@inject NavigationManager urlNavigationManager

<h2>Delete Employee</h2>
<h3>Are you sure you want to delete employee with id : @empID</h3>
<br />

<div class="col-md-4">
    <table class="table">
        <tr>
            <td>Name</td>
            <td>@emp.Name</td>
        </tr>
        <tr>
            <td>Gender</td>
            <td>@emp.Gender</td>
        </tr>
        <tr>
            <td>Department</td>
            <td>@emp.Department</td>
        </tr>
        <tr>
            <td>City</td>
            <td>@emp.City</td>
        </tr>
    </table>
    <div class="form-group">
        <button class="btn btn-danger" @onclick="(async () => await Delete())">Delete</button>
        <button class="btn btn-light" @onclick="Cancel">Cancel</button>
    </div>
</div>

@code { [Parameter]
    public int empID { get; set; }

    Employee emp = new Employee();

    protected override async Task OnInitializedAsync()
    {
        emp = await Http.GetJsonAsync<Employee>("/api/Employee/Details/" + empID);
    }

    protected async Task Delete()
    {
        await Http.DeleteAsync("api/Employee/Delete/" + Convert.ToInt32(empID));
        urlNavigationManager.NavigateTo("/fetchemployee");
    }

    void Cancel()
    {
        urlNavigationManager.NavigateTo("/fetchemployee");
    }
}

The route for this page is also parametrized since we are fetching the record of the employee on page load.

The HTML part will display the employee data and ask the user for a confirmation to delete the employee record.

Inside the @code section we are fetching the employee records in OnInitAsync method based on the employeeID passed in the parameter. This will display the employee records as the page loads.

The Delete method will be invoked on clicking “Delete” button, which will send a delete request to our API along with the employee ID of the employee to be deleted. On successful deletion the user will be navigated back to FetchEmployee page.

Navigation menu for our module (app.)

Open BlazorCrud.Client/Shared/ NavMenu.razor file and put the following code in it.

@* J:\netcore\source\repos\BlazorCrudEMP\BlazorCrud\Client\Shared\NavMenu.razor *@
<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorCrud</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">

        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>

        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchemployee">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch employee
            </NavLink>
        </li>

    </ul>
</div>

@code { private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

 

URL R : https://localhost:5001/fetchemployee – URL has “/fetchemployee” appended to it as we have defined it using @page directive.

URL C : https://localhost:5001/addemployee

URL U and D : https://localhost:5001/editemployee/2 – simmilar to releteemployee/2

 

 

WPF :

https://www.youtube.com/watch?v=LUKp76CNmJY         https://goo.gl/Axoeja

https://www.youtube.com/watch?v=NiGsgctfiYM           https://goo.gl/fAUCRh