In this tutorial, we will learn a clean and simple way to implement Razor Page CRUD in Asp.Net Core with jQuery Ajax and Bootstrap Modal. This is the Entity Management Set of CRUD operations which doesn't reload the Pages, we can call it Single Page Application.
Table of Contents:
- Scope
- The Architecture
- Getting Started with Razor Page CRUD in ASP.NET Core
- Setting up the Project
- Setting up the Core Layer
- Setting up the Infrastructure Layer
- Setting up the Asp.Net Core Project
- Rendering Razor Partial View to String
- Setting up the Project
- Setting up the Core Layer
- Setting up the Infrastructure Layer
- Setting up the Asp.Net Core Project
- Rendering Razor Partial View to String
Scope
There are the things we will be implementing in our CRUD Application. This will be an Employee Management System.
- Data Annotations for Validations
- jQuery Validations and Unobstructive Validations - Client Side Validations
- Bootstrap Modal
- Dyanamic Loading of Partial Views via Ajax Calls
- Repository Pattern with Unit of. Work
- Onion Architecture Solutions
- jQuery Datatables
The Architecture
To keep things simple and clean we will be using onion architecture with inverted dependencies.
We will create three layers in this project repectively Web, Core and Infrastructure.
Getting Started with Razor Page CRUD in Asp.Net Core 3.1
Let's create the required projects, here I am creating the Web project first using Visual Studio 2019
for Mac.
Setting up the Projects
Setting up the Core Layer
Let's add a new project withing the same solution and name it Core. This will be created as .Net Core Library 3.1
Let's add two folders Entities and Interfaces in the Core Project. And under the Entities folder, create a new class and name it Employee
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Employee | |
{ | |
[Key] | |
public int Id { get; set; } | |
[Required] | |
[MinLength(2)] | |
public string FirstName { get; set; } | |
public string LastName { get; set; } | |
[Required] | |
[EmailAddress] | |
public string Email { get; set; } | |
[Required] | |
public int Mobile { get; set; } | |
public string Company { get; set; } | |
} |
To follow the Repository pattern, we will add a new interface and name it IGenericRepositoryAsync under the Interfaces folder in Core Proejct Layer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IGenericRepositoryAsync<T> where T : class | |
{ | |
Task<T> GetByIdAsync(int id); | |
Task<IReadOnlyList<T>> GetAllAsync(); | |
Task<T> AddAsync(T entity); | |
Task UpdateAsync(T entity); | |
Task DeleteAsync(T entity); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IEmployeeRepositoryAsync : IGenericRepositoryAsync<Employee> | |
{ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IUnitOfWork : IDisposable | |
{ | |
Task<int> Commit(); | |
} |
We have completed everything for the Core Project Layer, now let's move to create Infrastructure Project Layer as we did for the Core Project layer.
Setting up the Infrastructure Project Layer
Let's install the below-required packages for Infrastructure Layer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Install-Package Microsoft.EntityFrameworkCore | |
Install-Package Microsoft.EntityFrameworkCore.Tools | |
Install-Package MySql.Data | |
Install-Package MySql.Data.EntityFrameworkCore | |
Install-Package Microsoft.EntityFrameworkCore.Design | |
Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design |
Now that you have Entity Framework Core installed at the Infrastructure Layer, let’s add the ApplicationDbContext in-order to access the database. (We will be setting the connection string to the database later in this article at the Web Layer.)
Create a new Folder in the Infrastructure Layer and name it Data. Here add a new Class and name it ApplicationDbContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ApplicationDbContext : DbContext | |
{ | |
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) | |
{ | |
} | |
public DbSet<Employee> Employees { get; set; } | |
} |
Add a new folder in the Infrastructure layer and name it Repositories. Here add a new class, GenericRepositoryAsync.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class GenericRepositoryAsync<T> : IGenericRepositoryAsync<T> where T : class | |
{ | |
private readonly ApplicationDbContext _dbContext; | |
public GenericRepositoryAsync(ApplicationDbContext dbContext) | |
{ | |
_dbContext = dbContext; | |
} | |
public virtual async Task<T> GetByIdAsync(int id) | |
{ | |
return await _dbContext.Set<T>().FindAsync(id); | |
} | |
public async Task<T> AddAsync(T entity) | |
{ | |
await _dbContext.Set<T>().AddAsync(entity); | |
return entity; | |
} | |
public Task UpdateAsync(T entity) | |
{ | |
_dbContext.Entry(entity).State = EntityState.Modified; | |
return Task.CompletedTask; | |
} | |
public Task DeleteAsync(T entity) | |
{ | |
_dbContext.Set<T>().Remove(entity); | |
return Task.CompletedTask; | |
} | |
public async Task<IReadOnlyList<T>> GetAllAsync() | |
{ | |
return await _dbContext.Set<T>().ToListAsync(); | |
} | |
} |
As you can see from the above, almost all the CRUD operations ever needed for any entity is covered. T could be any class and you already have an entire class that could Perform Create, Read, Update and Delete Operations on the T Entity. But also see that we are not saving anything directly to the database via the Repository Implementation. Rather we will have a separate class that is responsible for Committing changes to the database. You should have heard about Unit Of Work, yeah?
Create another new class and name it UnitOfWork.cs in the Infrastructure layer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class UnitOfWork : IUnitOfWork | |
{ | |
private readonly ApplicationDbContext _dbContext; | |
private bool disposed; | |
public UnitOfWork(ApplicationDbContext dbContext) | |
{ | |
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); | |
} | |
public async Task<int> Commit() | |
{ | |
return await _dbContext.SaveChangesAsync(); | |
} | |
public void Dispose() | |
{ | |
Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
protected virtual void Dispose(bool disposing) | |
{ | |
if (!disposed) | |
{ | |
if (disposing) | |
{ | |
_dbContext.Dispose(); | |
} | |
} | |
disposed = true; | |
} | |
} |
Let's add the last implementation which is responsible for performing the CRUD operations specific to Employee Entity.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class EmployeeRepositoryAsync : GenericRepositoryAsync<Employee>, IEmployeeRepositoryAsync | |
{ | |
private readonly DbSet<Employee> _employee; | |
public EmployeeRepositoryAsync(ApplicationDbContext dbContext) : base(dbContext) | |
{ | |
_employee = dbContext.Set<Employee>(); | |
} | |
} |
Setting up the Asp.Net Core Project
Let's install the below-required packages for the Web project layer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Install-Package Microsoft.EntityFrameworkCore | |
Install-Package Microsoft.EntityFrameworkCore.Tools | |
Install-Package MySql.Data | |
Install-Package MySql.Data.EntityFrameworkCore | |
Install-Package Microsoft.EntityFrameworkCore.Design | |
Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design |
With that done, open up the Startup.cs. Here we will have to add the services/dependencies to our ASP.NET Core Container. Navigate to the ConfigureServices method and add the following. Note that you would have to fix the reference warnings that may occur.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.AddDbContext<ApplicationDbContext>(options => | |
options.UseMySQL( | |
Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName))); | |
#region Repositories | |
services.AddTransient(typeof(IGenericRepositoryAsync<>), typeof(GenericRepositoryAsync<>)); | |
services.AddTransient<IEmployeeRepositoryAsync, EmployeeRepositoryAsync>(); | |
services.AddTransient<IUnitOfWork, UnitOfWork>(); | |
#endregion | |
services.AddRazorPages(); | |
} |
We should add the connectionString in our appsettings.json file... So let's move to add ConnectionStrings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Logging": { | |
"LogLevel": { | |
"Default": "Information", | |
"Microsoft": "Warning", | |
"Microsoft.Hosting.Lifetime": "Information" | |
} | |
}, | |
"AllowedHosts": "*", | |
"ConnectionStrings": { | |
"DefaultConnection": "Server=localhost;Database=RazorCRUD;Uid=root;Pwd=root@123;" | |
} | |
} |
Now we need to add the migrations. if you want to know how to add Migrations so click here to Add Migrations
Let's install the jQuery Datatables into our Web project and add the reference for DataTables in _Layout.cshtml file as below.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<head> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>@ViewData["Title"] - Web</title> | |
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> | |
<link rel="stylesheet" href="~/css/site.css" /> | |
<link href="~/lib/datatables/css/dataTables.bootstrap4.min.css" rel="stylesheet" /> | |
</head> | |
//Just add the below scripts right above the @RenderSection("Scripts", required: false) | |
<script src="~/lib/datatables/js/jquery.dataTables.min.js"></script> | |
<script src="~/lib/datatables/js/dataTables.bootstrap4.min.js"></script> | |
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script> | |
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script> |
Since we are already here, let’s also add the skeleton of the Bootstrap Modal that we are going to use further in the article. Above the footer tag, add the HTML for the Modal. Note that the Modal Body is empty. This is because we will be filling it dynamically via jQuery AJAX with Razor Partial Views.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Add the below code just above the <footer> tag | |
<div class="modal fade" tabindex="-1" role="dialog" data-backdrop="static" data-keyboard="false" id="form-modal"> | |
<div class="modal-dialog modal-lg" role="document"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h5 class="modal-title"></h5> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
</div> | |
<div class="modal-body"> | |
</div> | |
</div> | |
</div> | |
</div> |
There is already a site.js file under the wwwroot/js folder. So let's add the below code. Here we'll add the 3 functions which will be required for CRUD operations and it's the important part of our project which allows us not to reload the pages.
GetAll() - Loads all the Employees from the database to the jQuery DataTables.
CreateOrEdit() - This functions do the Create or Edit an employee.
Delete() - It deletes an employee from the database.
Now the missing pieces to the puzzle are as follows:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(document).ready(function () { | |
jQueryModalGet = (url, title) => { | |
try { | |
$.ajax({ | |
type: 'GET', | |
url: url, | |
contentType: false, | |
processData: false, | |
success: function (res) { | |
$('#form-modal .modal-body').html(res.html); | |
$('#form-modal .modal-title').html(title); | |
$('#form-modal').modal('show'); | |
}, | |
error: function (err) { | |
console.log(err) | |
} | |
}) | |
//to prevent default form submit event | |
return false; | |
} catch (ex) { | |
console.log(ex) | |
} | |
} | |
jQueryModalPost = form => { | |
try { | |
$.ajax({ | |
type: 'POST', | |
url: form.action, | |
data: new FormData(form), | |
contentType: false, | |
processData: false, | |
success: function (res) { | |
if (res.isValid) { | |
$('#viewAll').html(res.html) | |
$('#form-modal').modal('hide'); | |
} | |
}, | |
error: function (err) { | |
console.log(err) | |
} | |
}) | |
return false; | |
} catch (ex) { | |
console.log(ex) | |
} | |
} | |
jQueryModalDelete = form => { | |
if (confirm('Are you sure to delete this record ?')) { | |
try { | |
$.ajax({ | |
type: 'POST', | |
url: form.action, | |
data: new FormData(form), | |
contentType: false, | |
processData: false, | |
success: function (res) { | |
$('#viewAll').html(res.html); | |
}, | |
error: function (err) { | |
console.log(err) | |
} | |
}) | |
} catch (ex) { | |
console.log(ex) | |
} | |
} | |
//prevent default form submit event | |
return false; | |
} | |
}); | |
1. Razor Partial View to Hold the jQuery Datatable.
2. Razor Partial View to Add / Edit Customer.
3. A way to convert Razor Views to String, so that jQuery AJAX can fetch this string and simply overwrite the existing div / DOM object on the HTML. (This was particularly tough to figure out.)
4. And finally, our C# Handler methods that will return the partial views as requested by the AJAX calls.
Let’s start with the _ViewAll.cshtml. This will hold the HTML table definition and also the script needed to activate jQuery Datatable. Under the Pages folder in the Web Project, Create a new Razor View (Empty) and name it _ViewAll.cshtml.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@using Core.Entities | |
@model IEnumerable<Employee> | |
<table class="table table-bordered" id="employeTable"> | |
<thead> | |
<tr> | |
<th>FirstName</th> | |
<th>LastName</th> | |
<th>Email</th> | |
<th>PhoneNumber</th> | |
<th>Company</th> | |
<th>Actions</th> | |
</tr> | |
</thead> | |
<tbody> | |
@if (Model.Count() != 0) | |
{ | |
@foreach (var employee in Model) | |
{ | |
<tr> | |
<td>@employee.FirstName</td> | |
<td>@employee.LastName</td> | |
<td>@employee.Email</td> | |
<td>@employee.Mobile</td> | |
<td>@employee.Company</td> | |
<td text-right"> | |
<a onclick="jQueryModalGet('?handler=CreateOrEdit&id=@employee.Id','Edit Customer')" class="btn btn-info text-white"> Edit</a> | |
<form method="post" asp-page="Index" asp-route-id="@employee.Id" asp-page-handler="Delete" onsubmit="return jQueryModalDelete(this)" class="d-inline"> | |
<button type="submit" class="btn btn-danger text-white"> Delete</button> | |
</form> | |
</td> | |
</tr> | |
} | |
} | |
</tbody> | |
</table> | |
<script>$(document).ready(function () { | |
$("#employeTable").DataTable(); | |
});</script> |
Next, Let’s create the Form, CreateOEdit that will have all the fields we require. This is again a straight forward piece of code snippet. Create a new Razor Page View (Empty) and name it _CreateOrEdit.cshtml and add in the following.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@using Core.Entities | |
@model Employee | |
<form id="create-form" method="post" asp-page="Index" asp-route-id="@Model.Id" asp-page-handler="CreateOrEdit" onsubmit="return jQueryModalPost(this);"> | |
<div class="form-group row"> | |
<label class="col-md-3 col-form-label">First Name</label> | |
<div class="col-md-9"> | |
<input type="text" autocomplete="off" asp-for="FirstName" name="FirstName" class="form-control"> | |
<span asp-validation-for="FirstName" class="text-danger"></span> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label class="col-md-3 col-form-label">Last Name</label> | |
<div class="col-md-9"> | |
<input type="text" autocomplete="off" asp-for="LastName" name="LastName" class="form-control"> | |
<span asp-validation-for="LastName" class="text-danger"></span> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label class="col-md-3 col-form-label">Email</label> | |
<div class="col-md-9"> | |
<input type="email" autocomplete="off" asp-for="Email" name="Email" class="form-control"> | |
<span asp-validation-for="Email" class="text-danger"></span> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label class="col-md-3 col-form-label">Mobile</label> | |
<div class="col-md-9"> | |
<input type="number" autocomplete="off" asp-for="Mobile" name="Mobile" class="form-control" /> | |
<span asp-validation-for="Mobile" class="text-danger"></span> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label class="col-md-3 col-form-label">Company</label> | |
<div class="col-md-9"> | |
<input type="text" autocomplete="off" asp-for="Company" name="Company" class="form-control" /> | |
<span asp-validation-for="Company" class="text-danger"></span> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<div class="col-md-3"> | |
</div> | |
</div> | |
<div class="form-group justify-content-between"> | |
<button type="button" class="btn btn-secondary close-button" data-dismiss="modal">Cancel</button> | |
<button type="submit" class="btn btn-primary save-button">Save</button> | |
</div> | |
</form> | |
<script type="text/javascript" language=javascript>$.validator.unobtrusive.parse(document);</script> |
Now we required the partial views, so let's add the following code in our existing Index.cshtml file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@page | |
@model IndexModel | |
@{ | |
ViewData["Title"] = "Home page"; | |
} | |
<div class="text-center"> | |
<h1 class="display-4">Razor CRUD</h1> | |
<p>Learn about <a href="https://code-town.blogspot.com" target="_blank">building Web apps with ASP.NET Core</a>.</p> | |
</div> | |
<div class="card"> | |
<div class="col-sm-12" style="padding:20px"> | |
<a onclick="jQueryModalGet('?handler=CreateOrEdit','Create Employee')" class="btn bg-success"> | |
Create | |
</a> | |
<a id="reload" class="btn bg-warning"> | |
Reload | |
</a> | |
</div> | |
<div id="viewAll" class="card-body table-responsive"></div> | |
</div> | |
@section Scripts | |
{ | |
<script>$(document).ready(function () { | |
$('#viewAll').load('?handler=ViewAllPartial'); | |
}); | |
$(function () { | |
$('#reload').on('click', function () { | |
$('#viewAll').load('?handler=ViewAllPartial'); | |
}); | |
});</script> | |
} |
Rendering Razor Partial View to String
Now, the question is, We have Razor Partial Views and AJAX, how do you convert Razor Partial Views to Strings / HTML. The answer is to create a service that can do so. In the Web Project add a new Folder and name it Services. And in it, create a new class and name it RazorRenderService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IRazorRenderService | |
{ | |
Task<string> ToStringAsync<T>(string viewName, T model); | |
} | |
public class RazorRenderService : IRazorRenderService | |
{ | |
private readonly IRazorViewEngine _razorViewEngine; | |
private readonly ITempDataProvider _tempDataProvider; | |
private readonly IServiceProvider _serviceProvider; | |
private readonly IHttpContextAccessor _httpContext; | |
private readonly IActionContextAccessor _actionContext; | |
private readonly IRazorPageActivator _activator; | |
public RazorRenderService(IRazorViewEngine razorViewEngine, | |
ITempDataProvider tempDataProvider, | |
IServiceProvider serviceProvider, | |
IHttpContextAccessor httpContext, | |
IRazorPageActivator activator, | |
IActionContextAccessor actionContext) | |
{ | |
_razorViewEngine = razorViewEngine; | |
_tempDataProvider = tempDataProvider; | |
_serviceProvider = serviceProvider; | |
_httpContext = httpContext; | |
_actionContext = actionContext; | |
_activator = activator; | |
} | |
public async Task<string> ToStringAsync<T>(string pageName, T model) | |
{ | |
var actionContext = | |
new ActionContext( | |
_httpContext.HttpContext, | |
_httpContext.HttpContext.GetRouteData(), | |
_actionContext.ActionContext.ActionDescriptor | |
); | |
using (var sw = new StringWriter()) | |
{ | |
var result = _razorViewEngine.FindPage(actionContext, pageName); | |
if (result.Page == null) | |
{ | |
throw new ArgumentNullException($"The page {pageName} cannot be found."); | |
} | |
var view = new RazorView(_razorViewEngine, | |
_activator, | |
new List<IRazorPage>(), | |
result.Page, | |
HtmlEncoder.Default, | |
new DiagnosticListener("RazorRenderService")); | |
var viewContext = new ViewContext( | |
actionContext, | |
view, | |
new ViewDataDictionary<T>(new EmptyModelMetadataProvider(), new ModelStateDictionary()) | |
{ | |
Model = model | |
}, | |
new TempDataDictionary( | |
_httpContext.HttpContext, | |
_tempDataProvider | |
), | |
sw, | |
new HtmlHelperOptions() | |
); | |
var page = (result.Page); | |
page.ViewContext = viewContext; | |
_activator.Activate(page, viewContext); | |
await page.ExecuteAsync(); | |
return sw.ToString(); | |
} | |
} | |
private IRazorPage FindPage(ActionContext actionContext, string pageName) | |
{ | |
var getPageResult = _razorViewEngine.GetPage(executingFilePath: null, pagePath: pageName); | |
if (getPageResult.Page != null) | |
{ | |
return getPageResult.Page; | |
} | |
var findPageResult = _razorViewEngine.FindPage(actionContext, pageName); | |
if (findPageResult.Page != null) | |
{ | |
return findPageResult.Page; | |
} | |
var searchedLocations = getPageResult.SearchedLocations.Concat(findPageResult.SearchedLocations); | |
var errorMessage = string.Join( | |
Environment.NewLine, | |
new[] { $"Unable to find page '{pageName}'. The following locations were searched:" }.Concat(searchedLocations)); | |
throw new InvalidOperationException(errorMessage); | |
} | |
} |
Let's add the below services into the Startup.cs as in ConfigureServices method.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services.AddHttpContextAccessor(); | |
services.AddTransient<IActionContextAccessor, ActionContextAccessor>(); | |
services.AddScoped<IRazorRenderService, RazorRenderService>(); |
This is the final part of the puzzle. The actual Index.cs. Open it up and add the following.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class IndexModel : PageModel | |
{ | |
private readonly IEmployeeRepositoryAsync _employee; | |
private readonly IUnitOfWork _unitOfWork; | |
private readonly IRazorRenderService _renderService; | |
private readonly ILogger<IndexModel> _logger; | |
public IndexModel(ILogger<IndexModel> logger, IEmployeeRepositoryAsync employee, IUnitOfWork unitOfWork, IRazorRenderService renderService) | |
{ | |
_logger = logger; | |
_employee = employee; | |
_unitOfWork = unitOfWork; | |
_renderService = renderService; | |
} | |
public IEnumerable<Employee> Employees { get; set; } | |
public void OnGet() | |
{ | |
} | |
public async Task<PartialViewResult> OnGetViewAllPartial() | |
{ | |
Employees = await _employee.GetAllAsync(); | |
return new PartialViewResult | |
{ | |
ViewName = "_ViewAll", | |
ViewData = new ViewDataDictionary<IEnumerable<Employee>>(ViewData, Employees) | |
}; | |
} | |
public async Task<JsonResult> OnGetCreateOrEditAsync(int id = 0) | |
{ | |
if (id == 0) | |
return new JsonResult(new { isValid = true, html = await _renderService.ToStringAsync("_CreateOrEdit", new Employee()) }); | |
else | |
{ | |
var thisEmployee = await _employee.GetByIdAsync(id); | |
return new JsonResult(new { isValid = true, html = await _renderService.ToStringAsync("_CreateOrEdit", thisEmployee) }); | |
} | |
} | |
public async Task<JsonResult> OnPostCreateOrEditAsync(int id, Employee employee) | |
{ | |
if (ModelState.IsValid) | |
{ | |
if (id == 0) | |
{ | |
await _employee.AddAsync(employee); | |
await _unitOfWork.Commit(); | |
} | |
else | |
{ | |
await _employee.UpdateAsync(employee); | |
await _unitOfWork.Commit(); | |
} | |
Employees = await _employee.GetAllAsync(); | |
var html = await _renderService.ToStringAsync("_ViewAll", Employees); | |
return new JsonResult(new { isValid = true, html = html }); | |
} | |
else | |
{ | |
var html = await _renderService.ToStringAsync("_CreateOrEdit", employee); | |
return new JsonResult(new { isValid = false, html = html }); | |
} | |
} | |
public async Task<JsonResult> OnPostDeleteAsync(int id) | |
{ | |
var employee = await _employee.GetByIdAsync(id); | |
await _employee.DeleteAsync(employee); | |
await _unitOfWork.Commit(); | |
Employees = await _employee.GetAllAsync(); | |
var html = await _renderService.ToStringAsync("_ViewAll", Employees); | |
return new JsonResult(new { isValid = true, html = html }); | |
} | |
} |
Now you can build your all projects and run the application... :)
Please comment if you face any issue in code.
You can download the Source Code from GitHub
Read my previous blog on ASP.NET Core 3.1 MVC CRUD Operations
Below is a small video of my running application...
No comments:
Post a Comment