Blazor - CRUD Using PostgreSQL And Entity Framework Core

We will see the CRUD actions in Blazor framework with PostgreSQL database and Entity Framework Core. We will create a sample Patient app with all CRUD actions.

In this article, we will see the CRUD actions in Blazor Framework with a PostgreSQL database and Entity Framework Core. We will create a sample patient app with all CRUD actions.

I already wrote an article in C# Corner about Blazor with CosmosDB.

This time, we are using PostgreSQL as a database. PostgreSQL is a powerful, open source object-relational database system. The origin of PostgreSQL date back to 1986 as a part of the POSTGRES project at the University of California at Berkeley and has more than 30 years of active development on the core platform. It is very easy to use and high-performance database and absolutely free!

Please refer to this URL to get more details about PostgreSQL.

Step 1 - Create a database in PostgreSQL

Before creating a new database, I will create a login role in PostgreSQL. I am using pgAdmin3 to administer PostgreSQL.

Please log into pgAdmin3 with default credentials and create a new “Login Role”.

Database in PostgreSQL
I am creating this login role as “Super User”.

Database in PostgreSQL 

After clicking the OK button, our login role will be created successfully.

Now, we can create a new database from the database tab.

Database in PostgreSQL 
Please give a valid database name. We can set any login role as “Owner” to this database. I am giving all privileges to this database.

Database in PostgreSQL 

Our new database is created now.

If you check the database details in pgAdmin3, you can see the below details.

Database in PostgreSQL

Step 2 - Create a new "patients” tables for our Patient App

We can create a new “patients” table in our database. For that, we can use the SQL editor option in pgAdmin3.

Database in PostgreSQL
 
Please use the below script to create a new table inside the database.
  1. CREATE TABLE public.patients  
  2. (  
  3.   id character varying(50) NOT NULL,  
  4.   name character varying(200) NOT NULL,  
  5.   address character varying(500),  
  6.   city character varying(100),  
  7.   age numeric NOT NULL,  
  8.   gender character varying(10),  
  9.   CONSTRAINT patient_pkey PRIMARY KEY (id)  
  10. );  
  11.   
  12. ALTER TABLE public.patients  
  13.   OWNER TO sarath;  

Please note that in PostgreSQL, all the column names are created in lowercase letter.s Even if you write the script in uppercase letters, objects will be created in lowercase only.

Our table is ready. Now, we can create our Blazor project.

Step 3 - Create Patient app project with Blazor (ASP.NET Core Hosted) template.

Please refer to my previous article to check the prerequisites for the 
Blazor project.

Database in PostgreSQL 

We can choose 
Blazor (ASP.NET Core Hosted) template.

Database in PostgreSQL 

Our new project will be ready shortly.

Please note that by default, our solution has 3 projects.

Database in PostgreSQL 

Client“ project contains all the client-side scripts and library files; “Server” project contains our Web API part (controllers and other business logic); and “Shared” project contains all the commonly shared classes like models.

We will use Entity Framework Core to establish a connection between PostgreSQL and our .NET Core application.

Please install “Microsoft.EntityFrameworkCore.Tools” and “Npgsql.EntityFrameworkCore.PostgreSQL” (for PostgreSQL) NuGet packages in “Server” project.

Database in PostgreSQL 

Database in PostgreSQL 

In our solution
, some classes and Razor pages are automatically created at the project creation time. We can remove the unwanted files now. We can create a Patient model class now in the Shared project. Please create a Models folder and create a Patient class inside this folder.

Patient.cs
  1. namespace BlazorPostgresCRUD.Shared.Models  
  2. {  
  3.     public class Patient  
  4.     {  
  5.         public string id { get; set; }  
  6.   
  7.         public string name { get; set; }  
  8.   
  9.         public string address { get; set; }  
  10.   
  11.         public string city { get; set; }  
  12.   
  13.         public float age { get; set; }  
  14.   
  15.         public string gender { get; set; }  
  16.     }  
  17. }  

Now we can create the interface “IDataAccessProvider”. This interface will implement the CRUD actions inside the DataAccessProvider class.

IDataAccessProvider.cs.
  1. using System.Collections.Generic;  
  2.   
  3. namespace BlazorPostgresCRUD.Shared.Models  
  4. {  
  5.     public interface IDataAccessProvider  
  6.     {  
  7.         void AddPatientRecord(Patient patient);  
  8.         void UpdatePatientRecord(Patient patient);  
  9.         void DeletePatientRecord(string id);  
  10.         Patient GetPatientSingleRecord(string id);  
  11.         List<Patient> GetPatientRecords();  
  12.     }  
  13. }  

We can create “DomainModelPostgreSqlContextunder new “DataAccess” folder in our Server project. This class will provide the PostgreSQL context to our application.

DomainModelPostgreSqlContext.cs
  1. using BlazorPostgresCRUD.Shared.Models;  
  2. using Microsoft.EntityFrameworkCore;  
  3.   
  4. namespace BlazorPostgresCRUD.Server.DataAccess  
  5. {  
  6.     public class DomainModelPostgreSqlContext : DbContext  
  7.     {  
  8.         public DomainModelPostgreSqlContext(DbContextOptions<DomainModelPostgreSqlContext> options) : base(options)  
  9.         {  
  10.         }  
  11.   
  12.         public DbSet<Patient> patients { getset; }  
  13.   
  14.         protected override void OnModelCreating(ModelBuilder builder)  
  15.         {  
  16.             base.OnModelCreating(builder);  
  17.         }  
  18.   
  19.         public override int SaveChanges()  
  20.         {  
  21.             ChangeTracker.DetectChanges();  
  22.             return base.SaveChanges();  
  23.         }  
  24.   
  25.     }  
  26. }  

We can create the “DataAccessPostgreSqlProvider” class now. This class will implement the “IDataAccessProvider” interface. This class provides all the CRUD actions to our Web API Controller (We will create this controller soon).

DataAccessPostgreSqlProvider.cs
  1. using BlazorPostgresCRUD.Shared.Models;  
  2. using Microsoft.Extensions.Logging;  
  3. using System.Collections.Generic;  
  4. using System.Linq;  
  5.   
  6. namespace BlazorPostgresCRUD.Server.DataAccess  
  7. {  
  8.     public class DataAccessPostgreSqlProvider : IDataAccessProvider  
  9.     {  
  10.         private readonly DomainModelPostgreSqlContext _context;  
  11.         private readonly ILogger _logger;  
  12.   
  13.         public DataAccessPostgreSqlProvider(DomainModelPostgreSqlContext context, ILoggerFactory loggerFactory)  
  14.         {  
  15.             _context = context;  
  16.             _logger = loggerFactory.CreateLogger("DataAccessPostgreSqlProvider");  
  17.         }  
  18.   
  19.         public void AddPatientRecord(Patient patient)  
  20.         {  
  21.             _context.patients.Add(patient);  
  22.             _context.SaveChanges();  
  23.         }  
  24.   
  25.         public void UpdatePatientRecord(Patient patient)  
  26.         {  
  27.             _context.patients.Update(patient);  
  28.             _context.SaveChanges();  
  29.         }  
  30.   
  31.         public void DeletePatientRecord(string id)  
  32.         {  
  33.             var entity = _context.patients.First(t => t.id == id);  
  34.             _context.patients.Remove(entity);  
  35.             _context.SaveChanges();  
  36.         }  
  37.   
  38.         public Patient GetPatientSingleRecord(string id)  
  39.         {  
  40.             return _context.patients.First(t => t.id == id);  
  41.         }  
  42.   
  43.         public List<Patient> GetPatientRecords()  
  44.         {  
  45.             return _context.patients.ToList();  
  46.         }  
  47.   
  48.     }  
  49. }  

We have defined all the CRUD actions inside this class.

Now, we can create our Web API controller. Please create a new API Class Controller.

Database in PostgreSQL 

Please add the below code to this Controller class.

PatientsController.cs
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Threading.Tasks;  
  5. using BlazorPostgresCRUD.Shared.Models;  
  6. using Microsoft.AspNetCore.Mvc;  
  7.   
  8. namespace BlazorPostgresCRUD.Server.Controllers  
  9. {  
  10.     public class PatientsController : Controller  
  11.     {  
  12.         private readonly IDataAccessProvider _dataAccessProvider;  
  13.   
  14.         public PatientsController(IDataAccessProvider dataAccessProvider)  
  15.         {  
  16.             _dataAccessProvider = dataAccessProvider;  
  17.         }  
  18.   
  19.         [HttpGet]  
  20.         [Route("api/Patients/Get")]  
  21.         public IEnumerable<Patient> Get()  
  22.         {  
  23.             return _dataAccessProvider.GetPatientRecords();  
  24.         }  
  25.   
  26.         [HttpPost]  
  27.         [Route("api/Patients/Create")]  
  28.         public void Create([FromBody]Patient patient)  
  29.         {  
  30.             if (ModelState.IsValid)  
  31.             {  
  32.                 Guid obj = Guid.NewGuid();  
  33.                 patient.id = obj.ToString();  
  34.                 _dataAccessProvider.AddPatientRecord(patient);  
  35.             }  
  36.         }  
  37.   
  38.         [HttpGet]  
  39.         [Route("api/Patients/Details/{id}")]  
  40.         public Patient Details(string id)  
  41.         {  
  42.             return _dataAccessProvider.GetPatientSingleRecord(id);  
  43.         }  
  44.   
  45.         [HttpPut]  
  46.         [Route("api/Patients/Edit")]  
  47.         public void Edit([FromBody]Patient patient)  
  48.         {  
  49.             if (ModelState.IsValid)  
  50.             {  
  51.                 _dataAccessProvider.UpdatePatientRecord(patient);  
  52.             }  
  53.         }  
  54.   
  55.         [HttpDelete]  
  56.         [Route("api/Patients/Delete/{id}")]  
  57.         public void DeleteConfirmed(string id)  
  58.         {  
  59.             _dataAccessProvider.DeletePatientRecord(id);  
  60.         }  
  61.     }  
  62. }  

Please note, we have initialized “IDataAccessProvider” interface inside this class. We can modify the “ConfigureServices” method inside the Startup class in Server project.

Database in PostgreSQL 

We have used dependency injection to initialize our 
DataAccessProvider class and added DBContext to our service. I have simply hard coded the PostgreSQL connection string in this class. You may try with a separate JSON configuration file if needed.

Our Shared project and Server project are ready. Now, we 
can modify the Client project. Please modify the “NavMenu” Razor View files under “Shared” folder with the below code.

NavMenu.cshtml
  1. <div class="top-row pl-4 navbar navbar-dark">  
  2.     <a class="navbar-brand" href="">Patient App</a>  
  3.     <button class="navbar-toggler" onclick=@ToggleNavMenu>  
  4.         <span class="navbar-toggler-icon"></span>  
  5.     </button>  
  6. </div>  
  7.   
  8. <div class=@(collapseNavMenu ? "collapse" : null) onclick=@ToggleNavMenu>  
  9.     <ul class="nav flex-column">  
  10.   
  11.         <li class="nav-item px-3">  
  12.             <NavLink class="nav-link" href="" Match=NavLinkMatch.All>  
  13.                 <span class="oi oi-home" aria-hidden="true"></span> Home  
  14.             </NavLink>  
  15.         </li>  
  16.   
  17.         <li class="nav-item px-3">  
  18.             <NavLink class="nav-link" href="/listpatients">  
  19.                 <span class="oi oi-list-rich" aria-hidden="true"></span> Patient Details  
  20.             </NavLink>  
  21.         </li>  
  22.     </ul>  
  23. </div>  
  24.   
  25. @functions {  
  26. bool collapseNavMenu = true;  
  27.   
  28. void ToggleNavMenu()  
  29. {  
  30.     collapseNavMenu = !collapseNavMenu;  
  31. }  
  32. }  

We can create ListPatients” Razor View under “Pages” folder now.

Database in PostgreSQL 
 
Please add the below code in this Razor page.

ListPatients.cshtml
  1. @using BlazorPostgresCRUD.Shared.Models  
  2. @page "/listpatients"  
  3. @inject HttpClient Http  
  4.   
  5. <h1>Patient Details</h1>  
  6. <p>  
  7.     <a href="/addpatient">Create New Patient</a>  
  8. </p>  
  9. @if (patientList == null)  
  10. {  
  11.     <p><em>Loading...</em></p>  
  12. }  
  13. else  
  14. {  
  15.     <table class='table'>  
  16.         <thead>  
  17.             <tr>  
  18.                 <th>Name</th>  
  19.                 <th>Address</th>  
  20.                 <th>City</th>  
  21.                 <th>Age</th>  
  22.                 <th>Gender</th>  
  23.             </tr>  
  24.         </thead>  
  25.         <tbody>  
  26.             @foreach (var patient in patientList)  
  27.             {  
  28.                 <tr>  
  29.                     <td>@patient.name</td>  
  30.                     <td>@patient.address</td>  
  31.                     <td>@patient.city</td>  
  32.                     <td>@patient.age</td>  
  33.                     <td>@patient.gender</td>  
  34.                     <td>  
  35.                         <a href='/editpatient/@patient.id'>Edit</a>  
  36.                         <a href='/deletepatient/@patient.id'>Delete</a>  
  37.                     </td>  
  38.                 </tr>  
  39.             }  
  40.         </tbody>  
  41.     </table>  
  42. }  
  43. @functions {  
  44. Patient[] patientList;  
  45.   
  46. protected override async Task OnInitAsync()  
  47. {  
  48.     patientList = await Http.GetJsonAsync<Patient[]>("/api/Patients/Get");  
  49. }  
  50.   
  51. }  

Also, add the below three Razor Views in our Client project.

AddPatients.cshtml
  1. @using BlazorPostgresCRUD.Shared.Models  
  2. @page "/addpatient"  
  3. @inject HttpClient Http  
  4. @inject Microsoft.AspNetCore.Blazor.Services.IUriHelper UriHelper  
  5.   
  6. <h2>Create Patient</h2>  
  7. <hr />  
  8. <div class="row">  
  9.     <div class="col-md-4">  
  10.         <form>  
  11.             <div class="form-group">  
  12.                 <label for="Name" class="control-label">Name</label>  
  13.                 <input for="Name" class="form-control" bind="@patient.name" />  
  14.             </div>  
  15.             <div class="form-group">  
  16.                 <label for="Address" class="control-label">Address</label>  
  17.                 <input for="Address" class="form-control" bind="@patient.address" />  
  18.             </div>  
  19.             <div class="form-group">  
  20.                 <label for="City" class="control-label">City</label>  
  21.                 <input for="City" class="form-control" bind="@patient.city" />  
  22.             </div>  
  23.             <div class="form-group">  
  24.                 <label for="Age" class="control-label">Age</label>  
  25.                 <input for="Age" class="form-control" bind="@patient.age" />  
  26.             </div>  
  27.             <div class="form-group">  
  28.                 <label for="Gender" class="control-label">Gender</label>  
  29.                 <select for="Gender" class="form-control" bind="@patient.gender">  
  30.                     <option value="">-- Select Gender --</option>  
  31.                     <option value="Male">Male</option>  
  32.                     <option value="Female">Female</option>  
  33.                 </select>  
  34.             </div>  
  35.             <div class="form-group">  
  36.                 <input type="button" class="btn btn-default" onclick="@(async () => await CreatePatient())" value="Save" />  
  37.                 <input type="button" class="btn" onclick="@Cancel" value="Cancel" />  
  38.             </div>  
  39.         </form>  
  40.     </div>  
  41. </div>  
  42. @functions {  
  43.   
  44. Patient patient = new Patient();  
  45.   
  46. protected async Task CreatePatient()  
  47. {  
  48.     await Http.SendJsonAsync(HttpMethod.Post, "/api/Patients/Create", patient);  
  49.     UriHelper.NavigateTo("/listpatients");  
  50. }  
  51.   
  52. void Cancel()  
  53. {  
  54.     UriHelper.NavigateTo("/listpatients");  
  55. }  
  56. }  

EditPatient.cshtml

  1. @using BlazorPostgresCRUD.Shared.Models  
  2. @page "/editpatient/{id}"  
  3. @inject HttpClient Http  
  4. @inject Microsoft.AspNetCore.Blazor.Services.IUriHelper UriHelper  
  5.   
  6. <h2>Edit</h2>  
  7. <h4>Patient</h4>  
  8. <hr />  
  9. <div class="row">  
  10.     <div class="col-md-4">  
  11.         <form>  
  12.             <div class="form-group">  
  13.                 <label for="Name" class="control-label">Name</label>  
  14.                 <input for="Name" class="form-control" bind="@patient.name" />  
  15.             </div>  
  16.             <div class="form-group">  
  17.                 <label for="Address" class="control-label">Address</label>  
  18.                 <input for="Address" class="form-control" bind="@patient.address" />  
  19.             </div>  
  20.             <div class="form-group">  
  21.                 <label for="City" class="control-label">City</label>  
  22.                 <input for="City" class="form-control" bind="@patient.city" />  
  23.             </div>  
  24.             <div class="form-group">  
  25.                 <label for="Age" class="control-label">Age</label>  
  26.                 <input for="Age" class="form-control" bind="@patient.age" />  
  27.             </div>  
  28.             <div class="form-group">  
  29.                 <label for="Gender" class="control-label">Gender</label>  
  30.                 <select for="Gender" class="form-control" bind="@patient.gender">  
  31.                     <option value="">-- Select Gender --</option>  
  32.                     <option value="Male">Male</option>  
  33.                     <option value="Female">Female</option>  
  34.                 </select>  
  35.             </div>  
  36.             <div class="form-group">  
  37.                 <input type="button" value="Save" onclick="@(async () => await UpdatePatient())" class="btn btn-default" />  
  38.                 <input type="button" value="Cancel" onclick="@Cancel" class="btn" />  
  39.             </div>  
  40.         </form>  
  41.     </div>  
  42. </div>  
  43. @functions {  
  44.   
  45. [Parameter]  
  46. string id { get; set; }  
  47.   
  48. Patient patient = new Patient();  
  49.   
  50. protected override async Task OnInitAsync()  
  51. {  
  52.     patient = await Http.GetJsonAsync<Patient>("/api/Patients/Details/" + id);  
  53. }  
  54.   
  55. protected async Task UpdatePatient()  
  56. {  
  57.     await Http.SendJsonAsync(HttpMethod.Put, "api/Patients/Edit", patient);  
  58.     UriHelper.NavigateTo("/listpatients");  
  59.   
  60. }  
  61.   
  62. void Cancel()  
  63. {  
  64.     UriHelper.NavigateTo("/listpatients");  
  65. }  
  66.   
  67. }  

DeletePatient.cshtml

  1. @using BlazorPostgresCRUD.Shared.Models  
  2. @page "/deletepatient/{id}"  
  3. @inject HttpClient Http  
  4. @inject Microsoft.AspNetCore.Blazor.Services.IUriHelper UriHelper  
  5.   
  6. <h2>Delete</h2>  
  7. <p>Are you sure you want to delete this Patient with id :<b> @id</b></p>  
  8. <br />  
  9. <div class="col-md-4">  
  10.     <table class="table">  
  11.         <tr>  
  12.             <td>Name</td>  
  13.             <td>@patient.name</td>  
  14.         </tr>  
  15.         <tr>  
  16.             <td>Address</td>  
  17.             <td>@patient.address</td>  
  18.         </tr>  
  19.         <tr>  
  20.             <td>City</td>  
  21.             <td>@patient.city</td>  
  22.         </tr>  
  23.         <tr>  
  24.             <td>Age</td>  
  25.             <td>@patient.age</td>  
  26.         </tr>  
  27.         <tr>  
  28.             <td>Gender</td>  
  29.             <td>@patient.gender</td>  
  30.         </tr>  
  31.     </table>  
  32.     <div class="form-group">  
  33.         <input type="button" value="Delete" onclick="@(async () => await Delete())" class="btn btn-default" />  
  34.         <input type="button" value="Cancel" onclick="@Cancel" class="btn" />  
  35.     </div>  
  36. </div>  
  37. @functions {  
  38.   
  39. [Parameter]  
  40. string id { get; set; }  
  41.   
  42. Patient patient = new Patient();  
  43.   
  44. protected override async Task OnInitAsync()  
  45. {  
  46.     patient = await Http.GetJsonAsync<Patient>  
  47. ("/api/Patients/Details/" + id);  
  48. }  
  49.   
  50. protected async Task Delete()  
  51. {  
  52.     await Http.DeleteAsync("api/Patients/Delete/" + id);  
  53.     UriHelper.NavigateTo("/listpatients");  
  54. }  
  55.   
  56. void Cancel()  
  57. {  
  58.     UriHelper.NavigateTo("/listpatients");  
  59. }  
  60.   
  61. }  

Our entire application is ready now. We can check the work flow one by one.

Please run the application and create new Patient information now.


Database in PostgreSQL 

After creating the record, it will be automatically displayed in the List View.

Database in PostgreSQL


Now, we can edit the information.

Database in PostgreSQL 

I have edited the City field. If you check the pgAdmin3 SQL query, you can now get the data.

Database in PostgreSQL 

We can delete the data now.

Database in PostgreSQL 

We have seen all four CRUD actions in this sample Patient app.

In this article, we saw how to create a database in PostgreSQL and we created a new login role as well. Later, we created a “patients” tables with 6 columns. Then, we created a Blazor application with Entity Framework Core. We used the dependency injection to initialize the DataAccessProvider class inside our project.

We can see more Blazor projects with new exciting features in upcoming articles.