.NET Core Single-Page CRUD Application. It's a user-friendly, efficient, and seamless solution that empowers you to Create, Read, Update, or Delete user data, all from a single page. Say goodbye to the complexities of routing – we've streamlined everything for you!
Our ASP.NET Core Single-Page CRUD Application is a testament to the capabilities of the ASP.NET Core framework. You're getting a robust foundation for building CRUD applications with great user interface.
Whether you're a newcomer to ASP.NET Core or an experienced developer, this example application lets you focus on what matters most. Explore the code, customize it to your specific requirements, or build upon its streamlined structure.
AspNetCoreFull/User.Data.db
.Program.cs
file.
NOTE: In AspNetCoreFull.csproj
file all required tools and dependencies are added for Microsoft , Entity Core framework & their tools and SQLite database must be included for enabling dotnet to use them
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>2ac58585-d1b3-40ff-a284-c11f26889b31</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SQLite" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0" />
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
<None Update="User.Data.db">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
NOTE: Run the given CLI commands to install .NET packages and it would inject the packages in .csproj
file
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SQLite
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package System.Data.SQLite
NOTE: SQLite connection with user context and SeedData Initialization Code is added in Program.cs
which enables a connection between Model , Context and database.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using AspnetCoreFull.Data;
using AspnetCoreFull.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Add services to the container.
builder.Services.AddRazorPages();
// Sqlite database connection with UserContext class
builder.Services.AddDbContext<UserContext>(options =>
{
options.UseSqlite(builder.Configuration.GetConnectionString("UserContext") ?? throw new InvalidOperationException("Connection string 'UserContext' not found."));
}, ServiceLifetime.Scoped);
var app = builder.Build();
// Create a scope for the service provider and call the SeedData.Initialize method
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
SeedData.Initialize(services);
}
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
User.cs
to define the data structure and UserCRUD.cs
to perform CRUD operations, handle form submissions, and interact with the database through UserContext.cs
.
To access User CRUD app, click the Users (CRUD)
link in the left sidebar or add /CRUD/UserCRUD
to the URL.
AspNetCoreFull -> User.Data.db
.
The first thing you will see is a list of existing users. We have used Datatable in our user management table.
The data is seeded in database by Program.cs
and that data is shown by UserCRUD.cs
Model's OnGetAsync()
which gives user data and all other counts in widgets as well.
UserCRUD.cs
file uses Helpers from UserCRUDHelpers.cs
file for GenerateUserAvatar
and FormatUSPhoneNumber
helper functions.
Here you can see how we implemented datatable with .NET Core CRUD App.
@* Users DataTable *@
@if (Model.Users != null && Model.Users.Count > 0)
{
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Search Filter</h5>
</div>
<div class="card-datatable table-responsive pb-0">
<table id="userTable" class="table">
<thead class="table-light">
<tr>
<th></th>
<th>Id</th>
<th>User</th>
<th>Email</th>
<th>Verified</th>
<th>Contact</th>
<th>Role</th>
<th>Plan</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@foreach (var user in Model.Users)
{
<tr>
<td></td>
<td>@user.Id</td>
<td class="user-name text-nowrap">@Html.Raw(UserCRUDHelpers.GenerateUserAvatar(user))</td>
<td class="text-truncate">@user.Email</td>
<td>@if (user.IsVerified)
{
<i class='ri-shield-check-line ri-24px text-success'><span class="d-none">1</span></i>
}
else
{
<i class="ri-shield-line ri-24px text-danger"><span class="d-none">0</span></i>
}
<input type="hidden" class="user-verified-@user.Id" data-is-verified="@user.IsVerified" />
</td>
<td class="text-nowrap">@UserCRUDHelpers.FormatUSPhoneNumber(user.ContactNumber)</td>
<td class="text-capitalize">@user.SelectedRole</td>
<td class="text-capitalize">@user.SelectedPlan</td>
<td class="text-nowrap">
@* Edit Button *@
<button class="btn btn-sm btn-icon edit-user-button" data-bs-toggle="offcanvas"
data-bs-target="#editUserOffcanvas" id="@user.Id-editUser"><i
class="ri-edit-box-line ri-20px"></i></button>
@* Delete Button *@
<form method="post" asp-page-handler="Delete" asp-route-id="@user.Id" id="@user.Id-deleteForm"
onsubmit="showSuccessAlert('Deleted');" class="d-inline">
<button class="btn btn-sm btn-icon" id="@user.Id-deleteUser" onclick="showDeleteConfirmation('@user.Id')">
<i class="ri-delete-bin-7-line ri-20px"></i>
</button>
</form>
@* Dropdown Button *@
<button class="btn btn-sm btn-icon dropdown-toggle hide-arrow" data-bs-toggle="dropdown"><i
class="ri-more-2-line ri-20px"></i></button>
<div class="dropdown-menu dropdown-menu-end m-0">
<a href="/Apps/Users/View/Account" class="dropdown-item">View</a>
<a href="javascript:void(0);" class="dropdown-item">Suspend</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
else
{
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Search Filter</h5>
</div>
<div class="card-datatable table-responsive pb-0">
<table id="userTable" class="table">
<thead class="border-top">
<tr>
<th></th>
<th>Id</th>
<th>User</th>
<th>Email</th>
<th>Verified</th>
<th>Contact</th>
<th>Role</th>
<th>Plan</th>
<th>Action</th>
</tr>
</thead>
</table>
</div>
</div>
}
We have used datatables
only to handle UI , export options
, pagination
, search
, and page length
whereas all data feeding , updating or deleting is done by UserCRUD.cs
Model
Note: We have initialized datatables with column options to achieve better UI for User management app
user-crud.js
for making single page CRUD work where Edit and Delete Offcanvas or Modals are synced with data from database so that user can update or delete data from database with ease and without navigating to different pages.
The function OnGetAsync()
lists the data in datatable from Sqlite database and therefore the data is shown
public async Task OnGetAsync()
{
// Load all users from the database
Users = await _context.User.ToListAsync();
// Create select lists for available roles and plans
AvailableRolesSelectList = new SelectList(GetAvailableRoles());
AvailablePlansSelectList = new SelectList(GetAvailablePlans());
// Calculate the count of duplicate user names
DuplicateUserCount = Users.GroupBy(u => u.UserName).Sum(g => g.Count() - 1);
// Get the total user count
TotalUserCount = Users.Count;
// Get the count of verified users
VerifiedUserCount = Users.Count(u => u.IsVerified);
// Get the count of unverified users
UnverifiedUserCount = Users.Count(u => !u.IsVerified);
}
You can add new ones by clicking the Add New User
button (above the table on the right). On the Add user right sidebar, you will find a form that allows you to fill out the user's name, email, contact, role, plan and verification status.
We have provided an update/edit icon in table to update the user's information.
<!-- Create User Form Offcanvas -->
<div class="offcanvas offcanvas-end" tabindex="-1" id="createUserOffcanvas" aria-labelledby="createUserOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="createUserOffcanvasLabel">Create User</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0">
<form method="post" id="createUserForm">
... ...
</form>
</div>
</div>
<!-- Edit User Form Offcanvas -->
<div class="offcanvas offcanvas-end" tabindex="-1" id="editUserOffcanvas" aria-labelledby="editUserOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="editUserOffcanvasLabel">Edit User</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0">
<form method="post" asp-page-handler="EditOrUpdate" asp-route-id="" id="editUserForm">
... ...
</form>
</div>
</div>
On clicking , It would open Create User Offcanvas form which on submitting would call
OnPostAsync()
that would create new user in context which is then updated to database.
On clicking , It would open Create User Offcanvas form which would get user from context with help of its
id
and then display current data in edit offcanvas form using JS function .On submit, Edit form would call
OnPostEditOrUpdateAsync(int id)
which would make updates to user data if any and save it to context and database with help of TryUpdateModelAsync()
method.
_CreatUserOffcanvasPartial.cshtml
and _EditUserOffcanvasPartial.cshtml
for Edit and Delete Offcanvas or Modals included in UserCRUD.cshtml
file.
// The OnPostAsync method is called when the Create User form is submitted
public async Task OnPostAsync()
{
if (NewUser?.UserName != null && NewUser?.Email != null)
{
// Add a new user to the database
_context.User.Add(NewUser);
await _context.SaveChangesAsync();
return RedirectToPage();
}
return Page();
}
// The OnPostEditOrUpdateAsync method is called when the Edit User form is submitted
public async Task OnPostEditOrUpdateAsync(int id)
{
var userToUpdate = await _context.User.FindAsync(id);
if (userToUpdate == null)
{
return NotFound();
}
// Update user properties based on form data
string isVerifiedString = Request.Form["user.IsVerified"];
userToUpdate.IsVerified = (isVerifiedString == "on") ? true : false;
// Update the user in the database and save changes
await TryUpdateModelAsync(userToUpdate, "user", u => u.UserName, u => u.Email, u => u.IsVerified, u => u.ContactNumber, u => u.SelectedRole, u => u.SelectedPlan);
await _context.SaveChangesAsync();
return RedirectToPage();
}
// Function to handle the "Edit User" Offcanvas Modal
const handleEditUserModal = editButton => {
// Get the user details from the table
... ...
};
// Attach event listeners for "Edit User" buttons (pencil icon)
const editUserButtons = document.querySelectorAll("[id$='-editUser']");
editUserButtons.forEach(editButton => {
editButton.addEventListener('click', () => handleEditUserModal(editButton));
});
We are validating create/update form using the formValidation javascript plugin. After successfully validating the form, we perform on -> core.form.valid or core.form.invalid
depending on which , create or edit form is submitted and their respective functions are called.
JS for using form validation in Users CRUD .NET Core is given below which is similar for create/edit.
// Get the Create for validation
const createNewUserForm = document.getElementById('createUserForm');
// Initialize FormValidation for create user form
const fv = FormValidation.formValidation(createNewUserForm, {
fields: {
'NewUser.UserName': {
validators: {
notEmpty: {
message: 'Please enter a user name'
},
stringLength: {
min: 6,
max: 30,
message: 'The user name must be more than 6 and less than 30 characters long'
}
}
},
...
},
plugins: {
...
}
})
.on('core.form.valid', function () {
// if fields are valid then
submitFormAndSetSuccessFlag(createNewUserForm, 'newUserFlag');
})
.on('core.form.invalid', function () {
// if fields are invalid
isFormValid = false;
});
To delete the user, we have provided a delete icon.
We have used sweetalert2
to get the delete confirmation.
<!-- Delete Button -->
<form method="post" asp-page-handler="Delete" asp-route-id="@user.Id" id="@user.Id-deleteForm"
onsubmit="showSuccessAlert('Deleted');" class="d-inline">
<button class="btn btn-sm btn-icon" id="@user.Id-deleteUser" onclick="showDeleteConfirmation('@user.Id')">
<i class="ri-delete-bin-7-line ri-20px"></i>
</button>
</form>
// Functions to handle the Delete User Sweet Alerts (Delete Confirmation)
function showDeleteConfirmation(userId) {
event.preventDefault(); // prevent form submit
const userName = document.querySelector(`.user-name-full-${userId}`).innerText;
Swal.fire({
...
}
// Sweet Alert Success Function (User Deleted/Created/Updated)
function showSuccessAlert(message) {
var name = message[0].toUpperCase() + message.slice(1);
Swal.fire({
...
}
});
}
For delete when is clicked then
showDeleteConfirmation(userId)
is called and if it is confirmed then OnPostDeleteAsync(int id)
is called which gets user from Id , deletes user and save changes to context/database
// The OnPostDeleteAsync method is called when the Delete User form is submitted
public async Task OnPostDeleteAsync(int id)
{
var user = await _context.User.FindAsync(id);
if (user != null)
{
// Remove the user from the database
_context.User.Remove(user);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
dotnet ef migrations add InitialCreate
dotnet ef database update
The migrations command generates code to create the initial database schema. The schema is based on the model specified in DbContext. The InitialCreate argument is used to name the migrations. Any name can be used, but by convention a name is selected that describes the migration.
The update command runs the Up method in migrations that have not been applied. In this case, update runs the Up method in the Migrations/
file, which creates the database.
Download Db browser for SQLite to view & manage database at SQlite Browser.
Here, ConnectionStrings=>Data Source
is set to Database name which is used in Program.cs
to establish connection between database and table allowing CRUD operations.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"UserContext": "Data Source=User.Data.db" // this should match your database name!
}
}
// Sqlite database connection with UserContext class
builder.Services.AddDbContext(options =>
{
options.UseSqlite(builder.Configuration.GetConnectionString("UserContext") ?? throw new InvalidOperationException("Connection string 'UserContext' not found."));
}, ServiceLifetime.Scoped);
var app = builder.Build();
// Create a scope for the service provider and call the SeedData.Initialize method
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
SeedData.Initialize(services);
}
To set up CRUD in starter kit (AspNetCoreStarter), consider the following steps:
/AspNetCoreFull
and paste in /AspNetCoreStarter
:
/Data
/Models
/Pages/CRUD
Program.cs
in /AspnetCoreStarter and then replace it with Program.cs
file from AspNetCoreFull.dotnet ef migrations add InitialCreate
dotnet ef database update
/Migrations
in which its corresponding context files will be created whereas User.Data.db
file will be created in /AspnetCoreStarter
.
User.Data.db-shm
& User.Data.db-wal
are generated along with User.Data.db
as its SQLite database enhancement files which help in caching or storing data and enable smooth experience.User.Data.db
from /AspnetCoreStarter and you can operate the database from browser itself!Now run your starter kit with dotnet run
or dotnet watch
and you will have CRUD in your starter kit!