CRUD Operations With Blazor In .NET 6

Introduction

In this article, we will discuss related how to perform crud operations with the blazor web assembly clients we've got a web API for our server project and additionally, we will also add and use entity framework core with a SQL server database.

Link to download the project source code here

Step 1

Let's create a Blazor WebAssembly App. Open Visual Studio 2022 and click on Create a new project.

CRUD Operations with Blazor in .NET 6

CRUD Operations with Blazor in .NET 6

Select .Net 6.0 Framework

CRUD Operations with Blazor in .NET 6

Step 2

Let's build a kind of a doctors database. Create the models with some properties.

Right-click on Shared Project and Create Wards and Doctors Model class as below

CRUD Operations with Blazor in .NET 6

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BlazorFullStackCrud.Shared
{
    public class Ward
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
    }
}

Also add Doctor class with the below properties

CRUD Operations with Blazor in .NET 6

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BlazorFullStackCrud.Shared
{
    public class Doctor
    {
        public int Id { get; set; }
        public string FirstName { get; set; } = string.Empty;
        public string LastName { get; set; } = string.Empty;
        public string Specialization { get; set; } = string.Empty;
        public Ward? Ward { get; set; }
        public int WardId { get; set; }
    }
}

Step 3

Let's work on data part. Create a folder named Data and add a DataContext class and inherit it from DbContext. Before that let's install the required nuget packages too.

CRUD Operations with Blazor in .NET 6

CRUD Operations with Blazor in .NET 6  

A DbContext instance represents a session with the database and can be used to query and save instances of your entities, DbContext is a combination of the Unit Of Work and Repository patterns.

Entity Framework Core does not support multiple parallel operations being run on the same DbContext instance. This includes both parallel executions of async queries and any explicit concurrent use from multiple threads.

Typically we create a class that derives from DbContext and contains DbSet<TEntty> properties foreach entity in the model. The DbSet<TEntity> properties have a public setter, they are automatically initialized when the instance of the derived context is created.

namespace BlazorFullStackCrud.Server.Data
{
    public class DataContext : DbContext
    {
        public DataContext(DbContextOptions<DataContext> options) : base(options)
        {

        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Ward>().HasData(
                new Ward { Id = 1, Name = "General" },
                new Ward { Id = 2, Name = "ICU" }
            );

            modelBuilder.Entity<Doctor>().HasData(
                new Doctor
                {
                    Id = 1,
                    FirstName = "Sukesh",
                    LastName = "D",
                    Specialization = "General Anesthesia",
                    WardId = 1,
                },
                new Doctor
                {
                    Id = 2,
                    FirstName = "Madhu",
                    LastName = "Pilli",
                    Specialization = "Orthopaedics",
                    WardId = 2
                }
            );
        }

        public DbSet<Doctor> Doctors { get; set; }

        public DbSet<Ward> Wards { get; set; }
    }
}

Step 4 - Now let's start by creating a controller and implement the get calls so the read operations for a list of doctors and then use entity framework and move the mock data to the database.

CRUD Operations with Blazor in .NET 6

CRUD Operations with Blazor in .NET 6

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace BlazorFullStackCrud.Server.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class DoctorController : ControllerBase
    {
        private readonly DataContext _context;

        public DoctorController(DataContext context)
        {
            _context = context;
        }

        [HttpGet]
        public async Task<ActionResult<List<Doctor>>> GetDoctors()
        {
            var doctors = await _context.Doctors.Include(sd => sd.Ward).ToListAsync();
            return Ok(doctors);
        }

        [HttpGet("wards")]
        public async Task<ActionResult<List<Ward>>> GetWards()
        {
            var wards = await _context.Wards.ToListAsync();
            return Ok(wards);
        }

        [HttpGet("{id}")]
        public async Task<ActionResult<Doctor>> GetSingleDoctor(int id)
        {
            var doctor = await _context.Doctors
                .Include(h => h.Ward)
                .FirstOrDefaultAsync(h => h.Id == id);
            if (doctor == null)
            {
                return NotFound("Sorry, no doctor here. :/");
            }
            return Ok(doctor);
        }

        [HttpPost]
        public async Task<ActionResult<List<Doctor>>> CreateDoctor(Doctor doctor)
        {
            doctor.Ward = null;
            _context.Doctors.Add(doctor);
            await _context.SaveChangesAsync();

            return Ok(await GetDbDoctors());
        }

        [HttpPut("{id}")]
        public async Task<ActionResult<List<Doctor>>> UpdateDoctor(Doctor doctor, int id)
        {
            var dbDoctor = await _context.Doctors
                .Include(sd => sd.Ward)
                .FirstOrDefaultAsync(sd => sd.Id == id);
            if (dbDoctor == null)
                return NotFound("Sorry, but no doctor for you. :/");

            dbDoctor.FirstName = doctor.FirstName;
            dbDoctor.LastName = doctor.LastName;
            dbDoctor.Specialization = doctor.Specialization;
            dbDoctor.WardId = doctor.WardId;

            await _context.SaveChangesAsync();

            return Ok(await GetDbDoctors());
        }

        [HttpDelete("{id}")]
        public async Task<ActionResult<List<Doctor>>> DeleteDoctor(int id)
        {
            var dbDoctor = await _context.Doctors
                .Include(sd => sd.Ward)
                .FirstOrDefaultAsync(sd => sd.Id == id);
            if (dbDoctor == null)
                return NotFound("Sorry, but no doctor for you. :/");

            _context.Doctors.Remove(dbDoctor);
            await _context.SaveChangesAsync();

            return Ok(await GetDbDoctors());
        }

        private async Task<List<Doctor>> GetDbDoctors()
        {
            return await _context.Doctors.Include(sd => sd.Ward).ToListAsync();
        }
    }
}

Step 5

Now let's create a service and add the CRUD as below.

Right-click on Services and create a folder named DoctorService

CRUD Operations with Blazor in .NET 6

namespace BlazorFullStackCrud.Client.Services.DoctorService
{
    public interface IDoctorService
    {
        List<Doctor> Doctors { get; set; }
        List<Ward> Wards { get; set; }
        Task GetWards();
        Task GetDoctors();
        Task<Doctor> GetSingleDoctor(int id);
        Task CreateDoctor(Doctor doctor);
        Task UpdateDoctor(Doctor doctor);
        Task DeleteDoctor(int id);
    }
}

CRUD Operations with Blazor in .NET 6  

using Microsoft.AspNetCore.Components;
using System.Net.Http.Json;

namespace BlazorFullStackCrud.Client.Services.DoctorService
{
    public class DoctorService : IDoctorService
    {
        private readonly HttpClient _http;
        private readonly NavigationManager _navigationManager;

        public DoctorService(HttpClient http, NavigationManager navigationManager)
        {
            _http = http;
            _navigationManager = navigationManager;
        }

        public List<Doctor> Doctors { get; set; } = new List<Doctor>();
        public List<Ward> Wards { get; set; } = new List<Ward>();

        public async Task CreateDoctor(Doctor doctor)
        {
            var result = await _http.PostAsJsonAsync("api/doctor", doctor);
            await SetDoctors(result);
        }

        private async Task SetDoctors(HttpResponseMessage result)
        {
            var response = await result.Content.ReadFromJsonAsync<List<Doctor>>();
            Doctors = response;
            _navigationManager.NavigateTo("doctors");
        }

        public async Task DeleteDoctor(int id)
        {
            var result = await _http.DeleteAsync($"api/doctor/{id}");
            await SetDoctors(result);
        }

        public async Task GetWards()
        {
            var result = await _http.GetFromJsonAsync<List<Ward>>("api/doctor/wards");
            if (result != null)
                Wards = result;
        }

        public async Task<Doctor> GetSingleDoctor(int id)
        {
            var result = await _http.GetFromJsonAsync<Doctor>($"api/doctor/{id}");
            if (result != null)
                return result;
            throw new Exception("Doctor not found!");
        }

        public async Task GetDoctors()
        {
            var result = await _http.GetFromJsonAsync<List<Doctor>>("api/doctor");
            if (result != null)
                Doctors = result;
        }

        public async Task UpdateDoctor(Doctor doctor)
        {
            var result = await _http.PutAsJsonAsync($"api/doctor/{doctor.Id}", doctor);
            await SetDoctors(result);
        }
    }
}

Step 6

Now create a form and this edit form always needs a model. We already had our Doctor Model and Handle submit as below.

CRUD Operations with Blazor in .NET 6

@page "/doctor"
@page "/doctor/{id:int}"
@inject IDoctorService DoctorService

@if (Id == null)
{
    <PageTitle>Create a new Doctor</PageTitle>
    <h3>Create a new Doctor</h3>
}
else
{
    <PageTitle>Edit @doctor.Specialization</PageTitle>
    <h3>Edit @doctor.Specialization</h3>
}

<EditForm Model="doctor" OnSubmit="HandleSubmit">
    <div>
        <label for="firstname">First Name</label>
        <InputText id="firstname" @bind-Value="doctor.FirstName" class="form-control"></InputText>
    </div>
    <div>
        <label for="lastname">Last Name</label>
        <InputText id="lastname" @bind-Value="doctor.LastName" class="form-control"></InputText>
    </div>
    <div>
        <label for="specialization">Specialization</label>
        <InputText id="specialization" @bind-Value="doctor.Specialization" class="form-control"></InputText>
    </div>
    <div>
        <label>Ward</label><br />
        <InputSelect @bind-Value="doctor.WardId" class="form-select">
            @foreach (var ward in DoctorService.Wards)
            {
                <option value="@ward.Id">@ward.Name</option>
            }
        </InputSelect>
    </div>
    <br />
    <button type="submit" class="btn btn-primary">@btnText</button>
    <button type="button" class="btn btn-danger" @onclick="DeleteDoctor">Delete Doctor</button>
</EditForm>

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

    string btnText = string.Empty;

    Doctor doctor = new Doctor { Ward = new Ward() };

    protected override async Task OnInitializedAsync()
    {
        btnText = Id == null ? "Save New Doctor" : "Update Doctor";
        await DoctorService.GetWards();
    }

    protected override async Task OnParametersSetAsync()
    {
        if (Id == null)
        {
            doctor.Ward = DoctorService.Wards[0];
            doctor.WardId = doctor.Ward.Id;
        }
        else
        {
            doctor = await DoctorService.GetSingleDoctor((int)Id);
        }
    }

    async Task HandleSubmit()
    {
        if (Id == null)
        {
            await DoctorService.CreateDoctor(doctor);
        }
        else
        {
            await DoctorService.UpdateDoctor(doctor);
        }
    }

    async Task DeleteDoctor()
    {
        await DoctorService.DeleteDoctor(doctor.Id);
    }
}

CRUD Operations with Blazor in .NET 6

@page "/doctors"
@inject IDoctorService DoctorService
@inject NavigationManager NavigationManager

<PageTitle>Doctors</PageTitle>

<h3>Doctors</h3>

<table class="table">
    <thead>
        <tr>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Specialization</th>
            <th>Ward</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var doctor in DoctorService.Doctors)
        {
            <tr>
                <td>@doctor.FirstName</td>
                <td>@doctor.LastName</td>
                <td>@doctor.Specialization</td>
                <td>@doctor.Ward.Name</td>
                <td>
                    <button class="btn btn-primary" @onclick="(() => ShowDoctor(doctor.Id))"><i class="oi oi-pencil"></i></button>
                </td>
            </tr>
        }
    </tbody>
</table>
<button class="btn btn-primary" @onclick="CreateNewDoctor">Create New Doctor</button>

@code {
    protected override async Task OnInitializedAsync()
    {
        await DoctorService.GetDoctors();
    }

    void ShowDoctor(int id)
    {
        NavigationManager.NavigateTo($"doctor/{id}");
    }

    void CreateNewDoctor()
    {
        NavigationManager.NavigateTo("/doctor");
    }
}

Here we can see the output.

CRUD Operations with Blazor in .NET 6

CRUD Operations with Blazor in .NET 6

Conclusion

In this article, we discussed how to perform CRUD operations in blazor web assembly and creating a database using the package manager console & commands. I hope you all enjoyed reading this and learned from it.


Similar Articles