CRUD Operations in Blazor WebApp in .Net 8.0
This application I have created using Blazor WebApp in .Net 8.0 with C# Language.
The following layers i have added in my application called Employee.
I have created a Dto called EmployeeDto in Employee.Shared project. Please find the following properties it had.
When we create a project, The blazor app will create a Client Project and Server project. By default, the server project has default files. Please find the screen shot below.
So, I have created the following pages for Employee. Called
Please find the screen shot below.
I have implemented Interface and Implementation class for CRUD operations. For that I have created class Library Project called Employee.Client.Infrastructure. Please find the screen shot below.
Please find the code for Interface called IEmployeeService.cs below.
using Employee.Shared;
namespace Employee.Client.Infrastructure.Interface
{
public interface IEmployeeService
{
Task<EmployeeDto> Add(EmployeeDto employeeDto);
Task<IEnumerable<EmployeeDto>> GetAllEmployees();
Task<EmployeeDto> GelEmployeeDetailsById(int employeeId);
Task<bool> Update(EmployeeDto employeeDto);
Task<bool> Delete(EmployeeDto employeeDto);
}
}
Please find the code for Implementation file called EmployeeService.cs below.
--------------------------------------------------------------------------------------------------------------
using Employee.Client.Infrastructure.Interface;
using Employee.Shared;
using System.Net.Http.Json;
namespace Employee.Client.Infrastructure.Implementation
{
public class EmployeeService : IEmployeeService
{
public HttpClient httpClient { get; set; }
public EmployeeService(HttpClient _httpClient)
{
httpClient = _httpClient;
}
public async Task<EmployeeDto> Add(EmployeeDto employeeDto)
{
var enployeeData = new EmployeeDto();
var result = await httpClient
.PostAsJsonAsync<EmployeeDto>("api/Employee", employeeDto);
if (result.IsSuccessStatusCode)
{
var data = await result.Content.ReadAsStringAsync();
}
return enployeeData;
}
public async Task<IEnumerable<EmployeeDto>> GetAllEmployees()
{
return await httpClient.GetFromJsonAsync<IEnumerable<EmployeeDto>>("api/Employee/GetAll");
}
public async Task<EmployeeDto> GelEmployeeDetailsById(int employeeId)
{
return await httpClient.GetFromJsonAsync<EmployeeDto>("api/Employee/GetEmployeeById/" + employeeId);
}
public async Task<bool> Update(EmployeeDto employeeDto)
{
bool isUpdated = false;
var enployeeData = new EmployeeDto();
var result = await httpClient
.PutAsJsonAsync<EmployeeDto>("api/Employee", employeeDto);
if (result.IsSuccessStatusCode)
{
var data = await result.Content.ReadAsStringAsync();
isUpdated = true;
}
return isUpdated;
}
public async Task<bool> Delete(EmployeeDto employeeDto)
{
bool isDeleted = false;
var enployeeData = new EmployeeDto();
var result = await httpClient.DeleteAsync("api/Employee/Delete/" + employeeDto.EmployeeId);
if (result.IsSuccessStatusCode)
{
isDeleted = true;
}
return isDeleted;
}
}
}
--------------------------------------------------------------------------------------------------------------
Once we create Interface and Implementation files, It is mandatory to Inject them to Service in PROGRAM.CS file in EmployeeWeb.Client project.
Please find the screen shot below.
Please find the List Page code here In the EmployeeWeb.Client Project
--------------------------------------------------------------------------------------------------------------------
@page "/employeeList"
@inject IEmployeeService iEmployeeService
@inject NavigationManager NavigationManager
@inject IJSRuntime JsRuntime
@rendermode @(new InteractiveAutoRenderMode(prerender: false))
@if (employeeList == null)
{
<p><em>Loading Data.................</em> </p>
}
else
{
<button @onclick="AddEmployee" class="btn btn-primary table-btn quick-add-btn"> +Add </button>
<table class="table table-bordered">
<thead>
<tr class="table-primary">
<th >Employee ID</th>
<th>First name</th>
<th>Last name</th>
<th>Email Id</th>
<th>Mobile Number</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var employee in employeeList)
{
<tr>
<td>@employee.EmployeeId</td>
<td>@employee.FirstName</td>
<td>@employee.LastName</td>
<td>@employee.Email</td>
<td>@employee.MobileNumber</td>
<td>
<a Edit href="@($"edit/{employee.EmployeeId}")" class="btn btn-sm btn-primary" tabindex="-1" role="button" aria-disabled="true">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-fill" viewBox="0 0 16 16">
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z">
</path>
</svg> Edit
</a>
<a class="btn btn-sm btn-danger" tabindex="-1" role="button" aria-disabled="true" @onclick="_ => DeleteEmployee(employee)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-fill" viewBox="0 0 16 16">
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"></path>
</svg> Delete
</a>
</td>
</tr>
}
</tbody>
</table>
}
@code {
public IEnumerable<EmployeeDto> employeeList { get; set; }
protected async override Task OnInitializedAsync()
{
await BindEmployeeDaya();
}
private async Task BindEmployeeDaya()
{
employeeList = await iEmployeeService.GetAllEmployees();
}
protected void AddEmployee()
{
NavigationManager.NavigateTo("/add");
}
private async Task DeleteEmployee(EmployeeDto employee)
{
bool confirmed = await JsRuntime.InvokeAsync<bool>("confirm", $"Are you sure to Delete the Employee Record : {employee.FirstName + " " + employee.LastName}?"); // Confirm
if (confirmed)
{
var result = await iEmployeeService.Delete(employee);
if (result)
{
JsRuntime.InvokeVoidAsync("window.alert", "Record Deleted successfully.");
await BindEmployeeDaya();
}
else
{
JsRuntime.InvokeVoidAsync("window.alert", "We are sorry..! we are unable to process your request now.");
}
}
}
}
---------------------------------------------------------------------------------------------------------------------
Please find the Add Page code In the EmployeeWeb.Client Project---------------------------------------------------------------------------------------------------------------------
@page "/add"
@using Employee.Shared.Validations.Employee
@inject IEmployeeService iEmployeeService
@inject IJSRuntime JSRuntime
@rendermode @(new InteractiveAutoRenderMode(prerender: false))
<div class="container" style="margin-left:5px;"></div>
<div class="row">
<div class="col-md-8 mx-auto">
<div class="card">
<div class="card-header bg-primary captionColor">Add Employee</div>
<div class="card-body">
<div class="col-lg-12">
<EditForm Model="@employee" OnValidSubmit="Saveemployee">
<DataAnnotationsValidator />
<div class="mb-3">
<label for="Name" class="form-label">First Name</label>
<div class="col-md-7">
<InputText class="form-control" @bind-Value="employee.FirstName" />
<ValidationMessage For="@(() => employee.FirstName)"></ValidationMessage>
</div>
</div>
<div class="mb-3">
<label for="Address" class="form-label">Last Name</label>
<div class="col-md-7">
<InputText class="form-control" @bind-Value="employee.LastName" />
</div>
</div>
<div class="mb-3">
<label for="Cellnumber" class="form-label">Email</label>
<div class="col-md-7">
<InputText class="form-control" @bind-Value="employee.Email" />
</div>
</div>
<div class="mb-3">
<label for="Emailid" class="form-label">Mobile Number</label>
<div class="col-md-7">
<InputText class="form-control" @bind-Value="employee.MobileNumber" />
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</EditForm>
</div>
</div>
</div>
</div>
</div>
@code {
protected EmployeeDto employee = new();
protected async Task Saveemployee()
{
var errorMessage = EmployeeValidations.VerifyEmployeeValidations(employee);
if (errorMessage == null)
{
var data = await iEmployeeService.Add(employee);
if (data != null)
{
await JSRuntime.InvokeVoidAsync("New employee added successfully");
}
}
else
{
await JSRuntime.InvokeVoidAsync("window.alert", errorMessage);
}
}
}
-----------------------------------------------------------------------------------------------------------------------
Please find the Edit Page code In the EmployeeWeb.Client Project
-----------------------------------------------------------------------------------------------------------------------
@page "/edit/{employeeId:int}"
@inject IEmployeeService iEmployeeService
@inject IJSRuntime jsRuntime
@rendermode @(new InteractiveAutoRenderMode(prerender: false))
<div class="container" style="margin-left:5px;">
<div class="row">
<div class="col-md-8 mx-auto">
<div class="card">
<div class="card-header bg-primary captionColor">Update Employee</div>
<div class="card-body">
<div class="col-lg-12">
<EditForm Model="@employee" OnValidSubmit="UpdateEmployee">
<DataAnnotationsValidator />
<div class="mb-3">
<label for="Name" class="form-label">First Name</label>
<div class="col-md-4">
<InputText class="form-control" @bind-Value="employee.FirstName" />
</div>
<ValidationMessage For="@(() => employee.FirstName)" />
</div>
<div class="mb-3">
<label for="Address" class="form-label">Last Name</label>
<div class="col-md-4">
<InputText class="form-control" @bind-Value="employee.LastName" />
</div>
<ValidationMessage For="@(() => employee.LastName)" />
</div>
<div class="mb-3">
<label for="Cellnumber" class="form-label">Email</label>
<div class="col-md-4">
<InputText class="form-control" @bind-Value="employee.Email" />
</div>
<ValidationMessage For="@(() => employee.Email)" />
</div>
<div class="mb-3">
<label for="Emailid" class="form-label">Mobile Number</label>
<div class="col-md-4">
<InputText class="form-control" @bind-Value="employee.MobileNumber" />
</div>
<ValidationMessage For="@(() => employee.MobileNumber)" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-warning">Update</button>
</div>
</EditForm>
</div>
</div>
</div>
</div>
</div>
</div>
@code {
[Parameter]
public int employeeId { get; set; }
protected EmployeeDto employee = new();
protected bool isRecordUpdated = false;
protected async override Task OnParametersSetAsync()
{
employee = await iEmployeeService.GelEmployeeDetailsById(employeeId);
}
protected async Task UpdateEmployee()
{
isRecordUpdated = await iEmployeeService.Update(employee);
if (isRecordUpdated)
{
jsRuntime.InvokeVoidAsync("window.alert", "Record updated successfully.");
}
}
}
-----------------------------------------------------------------------------------------------------------------------
Once you created pages, we are required to create a Controller for employee operations in Server project. Please find the screen shot below.
Create Folder called Controller in EmployeeWeb(Server Project). Right click on the Controller Folder , click on ADD and then click on Controller. Please find the screen shot below.
Now, please find the EmployeeApi Controller in the Server Project(EmployeeWeb)
---------------------------------------------------------------------------------------------------------------------------
using Employee.DataAccess.Interface;
using Employee.Shared;
using Microsoft.AspNetCore.Mvc;
namespace EmployeeWeb.Controller
{
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
public IEmployeeManager EmployeeManager { get; set; }
public EmployeeController(IEmployeeManager _EmployeeManager)
{
EmployeeManager = _EmployeeManager;
}
[HttpPost]
public async Task<ActionResult<EmployeeDto>> Add(EmployeeDto employeeDto)
{
var data = await EmployeeManager.Add(employeeDto);
return Ok(data);
}
[HttpGet]
[Route("GetAll")]
public async Task<List<EmployeeDto>> GetAllEmployees()
{
return await EmployeeManager.GetAllEmployees();
}
[HttpGet]
[Route("GetEmployeeById/{employeeId}")]
public async Task<EmployeeDto> GetEmployeeById(int employeeId)
{
return await EmployeeManager.GelEmployeeDetailsById(employeeId);
}
[HttpPut]
public async Task<ActionResult<EmployeeDto>> Update(EmployeeDto employeeDto)
{
var data = await EmployeeManager.Update(employeeDto);
return Ok(data);
}
[HttpDelete("Delete/{employeeId}")]
public async Task<IActionResult> Delete(int employeeId)
{
await EmployeeManager.Delete(employeeId);
return Ok();
}
}
}
-----------------------------------------------------------------------------------------------------------------------------
- In the above EmploeeApi controller , i have used IEmploeeManager Interface and EmployeeManager Service class for DB operations purpose.
- For DB Operations purpose , i have used Database First Approach with Entity Framework.
- For that , I have created a project called Employee.DataAccess.
- In the Employee.DataAccess project, i have created a folder called Data, Interface and Implementation folders.
Once we perform Database first approach, then all Database tables and DBContext file will be created in Data Folder -> Models Folder . Please find the scaffold connection string below.
Scaffold-DbContext "Data Source=(localdb)\ProjectsV13;Initial Catalog=Test1;Integrated Security=True" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Data/Models -Context EmploeeDBContext
If you want to add new table then use -Force command.
Scaffold-DbContext "Data Source=(localdb)\ProjectsV13;Initial Catalog=Test1;Integrated Security=True" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Data/Models -Context EmploeeDBContext
Please find the code for IEmployeeManager.cs class below.
namespace Employee.DataAccess.Interface
{
public interface IEmployeeManager
{
Task<EmployeeDto> Add(EmployeeDto employeeDto);
Task<List<EmployeeDto>> GetAllEmployees();
Task<EmployeeDto> GelEmployeeDetailsById(int employeeId);
Task<EmployeeDto> Update(EmployeeDto employeeDto);
Task<bool> Delete(int employeeId);
}
}
------------------------------------------------------------------------------------------------------------------
Please find the code for EmployeeManager.cs
------------------------------------------------------------------------------------------------------------------
using Employee.DataAccess.Data.Models;
using Employee.DataAccess.Interface;
using Employee.Shared;
using Microsoft.EntityFrameworkCore;
namespace Employee.DataAccess.Implementation
{
public class EmployeeManager : IEmployeeManager
{
private EmploeeDBContext DBContext { get; set; }
public EmployeeManager(EmploeeDBContext _DBContext)
{
DBContext = _DBContext;
}
public async Task<EmployeeDto> Add(EmployeeDto employeeDto)
{
var employee = new Employee.DataAccess.Data.Models.Employee
{
FirstName = employeeDto.FirstName,
LastName = employeeDto.LastName,
Email = employeeDto.Email,
MobileNo = employeeDto.MobileNumber,
};
try
{
DBContext.Employees.Add(employee);
await DBContext.SaveChangesAsync();
}
catch (Exception ex)
{
var errorMessage = ex.Message;
employeeDto = null;
}
return employeeDto;
}
public async Task<List<EmployeeDto>> GetAllEmployees()
{
var employeeList = new List<EmployeeDto>();
var data = await DBContext.Employees.ToListAsync();
foreach (var item in data)
{
var employee = new EmployeeDto()
{
EmployeeId = item.EmployeeId,
FirstName = item.FirstName,
LastName = item.LastName,
Email = item.Email,
MobileNumber = item.MobileNo,
};
employeeList.Add(employee);
}
return employeeList;
}
public async Task<EmployeeDto> GelEmployeeDetailsById(int employeeId)
{
var employeeData = await (DBContext.Employees.SingleOrDefaultAsync(x => x.EmployeeId == employeeId));
var employee = new EmployeeDto()
{
EmployeeId = employeeData.EmployeeId,
FirstName = employeeData.FirstName,
LastName = employeeData.LastName,
Email = employeeData.Email,
MobileNumber = employeeData.MobileNo,
};
return employee;
}
public async Task<EmployeeDto> Update(EmployeeDto employeeDto)
{
var empleyeeDtoData = new EmployeeDto();
var employeeDataToUpdate = DBContext.Employees.SingleOrDefault(x => x.EmployeeId == employeeDto.EmployeeId);
employeeDataToUpdate.EmployeeId = employeeDto.EmployeeId;
employeeDataToUpdate.FirstName = employeeDto.FirstName;
employeeDataToUpdate.LastName = employeeDto.LastName;
employeeDataToUpdate.Email = employeeDto.Email;
employeeDataToUpdate.MobileNo = employeeDto.MobileNumber;
if (employeeDataToUpdate != null)
{
try
{
await DBContext.SaveChangesAsync();
empleyeeDtoData.EmployeeId = employeeDataToUpdate.EmployeeId;
empleyeeDtoData.FirstName = employeeDataToUpdate.FirstName;
empleyeeDtoData.LastName = employeeDataToUpdate.LastName;
empleyeeDtoData.Email = employeeDataToUpdate.Email;
empleyeeDtoData.MobileNumber = employeeDataToUpdate.MobileNo;
}
catch (Exception ex)
{
var errorMessage = ex.Message;
employeeDto = null;
}
}
return empleyeeDtoData;
}
public async Task<bool> Delete(int employeeId)
{
if (DBContext.Employees == null)
{
return false;
}
var product = await DBContext.Employees.FindAsync(employeeId);
if (product == null)
{
return false;
}
DBContext.Employees.Remove(product);
await DBContext.SaveChangesAsync();
return true;
}
}
}
------------------------------------------------------------------------------------------------------------------
Once we create Interface and Implementation and started to using them in the API Controller, it is mandatory to register them with Service in the Program.cs file in the Employee Web Server Project.
For this, I have created, Service Dependency Injection folder in Server Project and registered that file in the Program.cs page.
------------------------------------------------------------------------------------------------------------------------------------------------------------------
using EmployeeWeb.Components;
using EmployeeWeb.ServerDependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddControllers();
builder.Services.AddScoped(http => new HttpClient
{
BaseAddress = new Uri(builder.Configuration.GetSection("BaseUrl").Value)
});
RegisterServices.AddDependencyInjection(builder);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}
app.UseHttpsRedirection();
app.MapControllers();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(EmployeeWeb.Client._Imports).Assembly);
app.Run();
-------------------------------------------------------------------------------------------------------------------------------