Critical Developers

Programmers Knowledge Base

CRUD operation in ASP.Net Core using jQuery Ajax and Bootstrap modal popup

In this post, we will learn how to perform CRUD operation in ASP.Net Core using jQuery Ajax and Bootstrap modal popup. So the question is why we want to perform CRUD using modal popups- the answer is sometimes we want CRUD on a single page or same page only. So lets start CRUD with bootstrap modal popups. Below are steps with detail code, you can also download the code in case you want to see demo.

In this example I am using SqlServer, jQuery Ajax, ASP.Net Core RazorPages. You can use ASP.Net Core MVC or jQuery Modal in similar way.

Follow the below steps to achieve same:-

Step-1: Create a SQL Table lets say "Products"

CREATE TABLE [dbo].[Products](
	[ProductId] [bigint] IDENTITY(1,1) PRIMARY KEY,
	[ProductName] [nvarchar](100) NOT NULL,
	[Category] [nvarchar](100) NULL,
	[Description] [nvarchar](1000) NULL,
	[UnitPrice] [decimal](18, 2) NOT NULL
)


Step-2: Create a ASP.Net Core Project (I am creating project with template to get jQuery & Bootstrap files in project automatically. We can add these files manually also.)


Step-3: Add a Model Class for Products as shown below

    public class Products
    {
        [Key]
        [Required]
        public long ProductId { get; set; }
        [Display(Name = "Product Name")]
        [Required, StringLength(100)]
        public string ProductName { get; set; }
        [Display(Name = "Category")]
        [Required, StringLength(100)]
        public string Category { get; set; }
        [Display(Name = "Description")]
        public string Description { get; set; }
        [Display(Name = "Unit Price")]
        [Required, RegularExpression(@"\d+(\.\d{1,2})?", ErrorMessage = "Invalid UnitPrice")]
        public decimal UnitPrice { get; set; }
    }


Step-4: Add a DBContext Class as shown below

    public class AppDBContext : DbContext
    {
        public AppDBContext(DbContextOptions<AppDBContext> options)
: base(options)
        {
        }

        public DbSet<Products> Products { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {

        }
    }


Step-5: Register DBContext Class in Startup.cs ConfigureServices

        public void ConfigureServices(IServiceCollection services)
        {
            var connection = Configuration.GetConnectionString("ConStr");
            services.AddDbContext<AppDBContext>(options=>options.UseSqlServer(connection));
            services.AddRazorPages();
        }


Step-6: Create a RazorPage "ManageProducts" with below Markups

@page
@model ManageProductsModel
@{
    ViewData["Title"] = "Manage Products";
}

<!-- Start: List Model-->
<div id="listModel">
    <div class="row">
        <div class="col-md-12">
            @if (!string.IsNullOrEmpty(Model.Message))
            {
                <div class="alert alert-primary">
                    @Model.Message
                </div>
            }
            <div class="card">
                <div class="box-header card-header">
                    <button type="button" class="btn btn-primary float-left pull-left" data-toggle="modal" data-target="#addProductModal">
                        Add Product
                    </button>
                    <form method="post" asp-page="ManageProducts" asp-page-handler="Search">
                        <input type="submit" name="btnSearch" id="btnSearch" class="btn btn-primary float-right pull-right" value="Search" />
                        <input type="text" name="searchTerm" id="searchTerm" class="form-control float-right pull-right" style="width:180px;margin-right:10px;" />
                    </form>
                </div>
                <div class="box-body card-body">
                    <table id="manageProduct" class="table table-striped">
                        <thead>
                            <tr>
                                <th scope="col">
                                    @Html.DisplayNameFor(model => model.ProductList[0].ProductName)
                                </th>
                                <th scope="col">
                                    @Html.DisplayNameFor(model => model.ProductList[0].Category)
                                </th>
                                <th scope="col">
                                    @Html.DisplayNameFor(model => model.ProductList[0].Description)
                                </th>
                                <th scope="col">
                                    @Html.DisplayNameFor(model => model.ProductList[0].UnitPrice)
                                </th>
                                <th scope="col">Action</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach (var item in Model.ProductList)
                            {
                            <tr>
                                <td>
                                    @Html.DisplayFor(modelItem => item.ProductName)
                                </td>
                                <td>
                                    @Html.DisplayFor(modelItem => item.Category)
                                </td>
                                <td>
                                    @Html.DisplayFor(modelItem => item.Description)
                                </td>
                                <td>
                                    @Html.DisplayFor(modelItem => item.UnitPrice)
                                </td>
                                <td>
                                    <a href="#editProductModal" class="edit" data-toggle="modal">Edit</a> |
                                    <a href="#viewProductModal" class="view" data-toggle="modal">View</a> |
                                    <a href="#deleteProductModal" class="delete" data-toggle="modal">Delete</a>
                                    <input type="hidden" name="productID" id="productID" asp-for="@item.ProductId" />
                                </td>
                            </tr>
                            }
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- End: List Model-->

<!-- Start: Add Modal-->
<div class="modal fade" id="addProductModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <form method="post" asp-page="ManageProducts" asp-page-handler="AddProduct">
                <div class="modal-header">
                    <h3 class="modal-title">Add New Product</h3>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                    <div class="form-group">
                        <label asp-for="Product.ProductName" class="control-label"></label>
                        <input asp-for="Product.ProductName" class="form-control" />
                        <span asp-validation-for="Product.ProductName" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Product.Category" class="control-label"></label>
                        <input asp-for="Product.Category" class="form-control" />
                        <span asp-validation-for="Product.Category" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Product.Description" class="control-label"></label>
                        <input asp-for="Product.Description" class="form-control" />
                        <span asp-validation-for="Product.Description" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Product.UnitPrice" class="control-label"></label>
                        <input asp-for="Product.UnitPrice" class="form-control" />
                        <span asp-validation-for="Product.UnitPrice" class="text-danger"></span>
                    </div>
                </div>
                <div class="modal-footer">
                    <input type="submit" value="Create" class="btn btn-success" />
                    <input type="button" value="Cancel" class="btn btn-primary" data-dismiss="modal" />
                </div>
            </form>
        </div>
    </div>
</div>
<!-- End: Add Modal-->

<!-- Start: Edit Modal-->
<div class="modal fade" id="editProductModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <form method="post" asp-page="ManageProducts" asp-page-handler="UpdateProduct">
                <div class="modal-header">
                    <h3 class="modal-title">Update Product</h3>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                    <input type="hidden" asp-for="Product.ProductId" />
                    <div class="form-group">
                        <label asp-for="Product.ProductName" class="control-label"></label>
                        <input asp-for="Product.ProductName" class="form-control" />
                        <span asp-validation-for="Product.ProductName" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Product.Category" class="control-label"></label>
                        <input asp-for="Product.Category" class="form-control" />
                        <span asp-validation-for="Product.Category" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Product.Description" class="control-label"></label>
                        <input asp-for="Product.Description" class="form-control" />
                        <span asp-validation-for="Product.Description" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Product.UnitPrice" class="control-label"></label>
                        <input asp-for="Product.UnitPrice" class="form-control" />
                        <span asp-validation-for="Product.UnitPrice" class="text-danger"></span>
                    </div>
                </div>
                <div class="modal-footer">
                    <input type="submit" value="Update" class="btn btn-success" />
                    <input type="button" value="Cancel" class="btn btn-primary" data-dismiss="modal" />
                </div>
            </form>
        </div>
    </div>
</div>
<!-- End: Edit Modal-->

<!-- Start: View Modal-->
    <div class="modal fade" id="viewProductModal">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h3 class="modal-title">View Product</h3>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div class="box box-primary">
                        <dl class="row">
                            <dt class="col-sm-6">
                                @Html.DisplayNameFor(model => model.Product.ProductName)
                            </dt>
                            <dd class="col-sm-6" id="Product_ProductName">
                            </dd>
                            <dt class="col-sm-6">
                                @Html.DisplayNameFor(model => model.Product.Category)
                            </dt>
                            <dd class="col-sm-6" id="Product_Category">
                            </dd>
                            <dt class="col-sm-6">
                                @Html.DisplayNameFor(model => model.Product.Description)
                            </dt>
                            <dd class="col-sm-6" id="Product_Description">
                            </dd>
                            <dt class="col-sm-6">
                                @Html.DisplayNameFor(model => model.Product.UnitPrice)
                            </dt>
                            <dd class="col-sm-6" id="Product_UnitPrice">
                            </dd>
                        </dl>
                    </div>
                </div>
                <div class="modal-footer">
                    <input type="button" value="Cancel" class="btn btn-primary" data-dismiss="modal" />
                </div>
            </div>
        </div>
    </div>
    <!-- End: View Modal-->

<!-- Start: Delete Modal-->
<div class="modal fade" id="deleteProductModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <form method="post" asp-page="ManageProducts" asp-page-handler="DeleteProduct">
                <div class="modal-header">
                    <h3 class="modal-title">Delete Product</h3>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <p>Are you sure you want to delete?</p>
                    <input type="hidden" name="productID" id="productID" />
                </div>
                <div class="modal-footer">
                    <input type="submit" value="Yes" class="btn btn-success" />
                    <input type="button" value="Cancel" class="btn btn-primary" data-dismiss="modal" />
                </div>
            </form>
        </div>
    </div>
</div>
<!-- End: Delete Modal-->

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}

    <script type="text/javascript">
    $(document).ready(function () {
        $("table#manageProduct .delete").click(function () {
            var productID = $(this).parent().find("#productID").val();
            $("#deleteProductModal").find("#productID").val(productID);
        });

        $("table#manageProduct .edit").click(function () {
            var productID = $(this).parent().find("#productID").val();
            $("#editProductModal").find("#Product_ProductId").val(productID);
            GetEditProductDetails(productID);
        });

        $("table#manageProduct .view").click(function () {
            var productID = $(this).parent().find("#productID").val();
            $("#viewProductModal").find("#Product_ProductId").val(productID);
            GetViewProductDetails(productID);
        });
    });

    function GetEditProductDetails(productID) {
        $.ajax({
            url: "@Url.Page("/ManageProducts","AjaxProductDetail")",
            typr: "GET",
            data: { "productID": "" + productID + "" },
            contentType: "application/json;charset=UTF-8",
            dataType: "json",
            success: function (response) {
                //alert(response);
                $('#editProductModal #Product_ProductName').val(response.productName);
                $('#editProductModal #Product_Category').val(response.category);
                $('#editProductModal #Product_Description').val(response.description);
                $('#editProductModal #Product_UnitPrice').val(response.unitPrice);
            },
            error: function (response) {
                alert(response.responseText);
                $('#editProductModal #Product_ProductName').val("");
                $('#editProductModal #Product_Category').val("");
                $('#editProductModal #Product_Description').val("");
                $('#editProductModal #Product_UnitPrice').val("");
            }
        });
        return false;
        }
    function GetViewProductDetails(productID) {
        $.ajax({
            url: "@Url.Page("/ManageProducts","AjaxProductDetail")",
            typr: "GET",
            data: { "productID": "" + productID + "" },
            contentType: "application/json;charset=UTF-8",
            dataType: "json",
            success: function (response) {
                //alert(response);
                $('#viewProductModal #Product_ProductName').text(response.productName);
                $('#viewProductModal #Product_Category').text(response.category);
                $('#viewProductModal #Product_Description').text(response.description);
                $('#viewProductModal #Product_UnitPrice').text(response.unitPrice);
            },
            error: function (response) {
                alert(response.responseText);
                $('#viewProductModal #Product_ProductName').text("");
                $('#viewProductModal #Product_Category').text("");
                $('#viewProductModal #Product_Description').text("");
                $('#viewProductModal #Product_UnitPrice').text("");
            }
        });
        return false;
    }
    </script>
}


Step-7: Implement CRUD in Razor PageModel (or in Controller in case of MVC)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using MyCoreDemo.Data;

namespace MyCoreDemo.Pages
{
    public class ManageProductsModel : PageModel
    {
        AppDBContext _context;
        public ManageProductsModel(AppDBContext context)
        {
            _context = context;
        }
        public List<Products> ProductList { get; set; }

        [BindProperty(SupportsGet = true)]
        public Products Product { get; set; }

        [BindProperty(SupportsGet = true)]
        public string SearchTerm { get; set; }

        [TempData]
        public string Message { get; set; }

        public void OnGet()
        {
            ProductList = _context.Products.ToList();
        }

        public void OnPostSearch()
        {
            if (!string.IsNullOrEmpty(SearchTerm))
            {
                ProductList = _context.Products.Where(p => p.ProductName.Contains(SearchTerm)).ToList();
            }
            else
            {
                ProductList = _context.Products.ToList();
            }
        }

        public IActionResult OnPostAddProduct()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
            //
            var existProduct = _context.Products.FirstOrDefault(e => e.ProductName == Product.ProductName && e.ProductId != Product.ProductId);
            if (existProduct != null)
            {
                Message = "Product " + Product.ProductName + " already exist";
                return RedirectToPage("/ManageProducts");
            }
            else
            {
                _context.Products.Add(Product);
                _context.SaveChanges();
            }
            //
            if (Product == null)
            {
                return RedirectToPage("Error");
            }
            Message = "Product " + Product.ProductName + " added successfully";
            return RedirectToPage("/ManageProducts");
        }

        public IActionResult OnGetAjaxProductDetail(int productID)
        {
            if (productID == 0)
            {
                return RedirectToPage("Error");
            }

            Product = _context.Products.First(p => p.ProductId == productID);

            if (Product == null)
            {
                return RedirectToPage("Error");
            }
            return new JsonResult(Product);
        }

        // Just for reference
        public void OnGetProductDetail(int productID)
        {
            Product = _context.Products.First(p => p.ProductId == productID);
        }

        public IActionResult OnPostUpdateProduct()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
            //
            var existProduct = _context.Products.FirstOrDefault(e => e.ProductName == Product.ProductName && e.ProductId != Product.ProductId);
            if (existProduct != null)
            {
                Message = "Product " + Product.ProductName + " already exist";
                return RedirectToPage("/ManageProducts");
            }
            else
            {
                _context.Products.Attach(Product).State = EntityState.Modified;
                _context.SaveChanges();
            }
            //
            if (Product == null)
            {
                return RedirectToPage("Error");
            }
            Message = "Product " + Product.ProductName + " updated successfully";
            return RedirectToPage("/ManageProducts");
        }

        public IActionResult OnPostDeleteProduct(int productID)
        {
            if (productID == 0)
            {
                return RedirectToPage("Error");
            }

            var Product = _context.Products.First(p => p.ProductId == productID);
            _context.Products.Remove(Product);
            _context.SaveChanges();

            if (Product == null)
            {
                return RedirectToPage("Error");
            }
            Message = "Product " + Product.ProductName + " deleted successfully";
            return RedirectToPage("/ManageProducts");
        }
    }
}


Step-8: Run the Project


Click here to download the project

Thats it !!

Redirect to Login Page if Session variable not exist in ASP.Net Core

In this article, we will learn how to check if session variable is exist and redirect to login page if session expires.

We can achieve this using middleware, so I will explain you with 2 methods:-

1) Using only Middleware (Simplest Method)

2) Using Policy and Middleware

Method-1: Using Middleware (Simplest Method)

Here we will add a small code in middleware in Startup.cs and the work done. It will check if session value is null or not and redirect to login page. See below code in bold-

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {

//... Other Middlewares

            app.UseSession();
            app.Use(async (context, next) =>
            {
                string CurrentUserIDSession= context.Session.GetString("CurrentUserID");
                if (!context.Request.Path.Value.Contains("/Account/Login"))
                {
                    if (string.IsNullOrEmpty(CurrentUserIDSession))
                    {
                        var path = $"/Account/Login?ReturnUrl={context.Request.Path}";
                        context.Response.Redirect(path);
                        return;
                    }

                }
                await next();
            });

//... Other Middlewares
        }

Note:- Register Session Service in ConfigureServices method of Startup.cs as shown below:-

        public void ConfigureServices(IServiceCollection services)
        {
            //... Other services

            services.AddSession(options =>
            {
                options.Cookie.Name = "_aspnetCoreSession";
                options.IdleTimeout = TimeSpan.FromMinutes(20);
                options.Cookie.IsEssential = true;
            });

            //... Other services
        }

Thats it !!


Method-2: Using Policy and Middleware

The logic behind is, we will create a policy "AuthorizeSessionPolicy" which will check session existence and return Fail and Succeed flag.

Then we will create a custom Middleware which will validate this policy, and if validation fails - HttpContext will redirect to Login Page with ReturnURL.

Note we are using Middleware to check the session globally which means in every request.

So refer below steps with code.

Steps:-

1) Create a Policy

2) Register the Policy in Startup.cs

3) Create a Custom Middleware Class which will check the policy

4) Call above Middleware in Configure method of Startup.cs to get called in every request


// =======================================================================
// Step-1: Create a Policy Class
// =======================================================================
public class AuthorizeWithSessionRequirement: IAuthorizationRequirement {
 public AuthorizeWithSessionRequirement() {

 }
}
public class AuthorizeSession: AuthorizationHandler < AuthorizeWithSessionRequirement > {
 SignInManager < User > _signInManager;
 SessionManager _sessionManager;

 public AuthorizeSession(
  SignInManager < User > signInManager,
  SessionManager sessionManager) {
  _signInManager = signInManager;
  _sessionManager = sessionManager;
 }
 protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
  AuthorizeWithSessionRequirement requirement) {
  string lsUserId = _sessionManager.CurrentUserId;

  if (string.IsNullOrEmpty(lsUserId)) {
   _signInManager.SignOutAsync();
   context.Fail();
  } else {
   context.Succeed(requirement);
  }

  //
  return Task.CompletedTask;
 }
}
// =======================================================================
// Step-2: Register Policy in Startup.cs
// =======================================================================
public void ConfigureServices(IServiceCollection services) {
 services.AddAuthorization(options => {
  options.AddPolicy("AuthorizeSessionPolicy", policy =>
   policy.AddRequirements(new AuthorizeWithSessionRequirement()));
 });
}
// =======================================================================
// Step-3: Create a Custom Middleware Class
// =======================================================================
public class CheckSessionExistMiddleware {
 private readonly RequestDelegate _next;
 public CheckSessionExistMiddleware(RequestDelegate next) {
  _next = next;
 }
 public async Task Invoke(HttpContext context) {
  if (!context.Request.Path.Value.Contains("/Account/Login")) {
   var endPoint = context.Features.Get < IEndpointFeature > () ? .Endpoint; // To get Page
   var authAttr = endPoint ? .Metadata ? .GetMetadata < AuthorizeAttribute > (); //To get Authorize attribute of page
   // To check if page has authorize attribute with policy as "PolicyName"
   if (authAttr != null && authAttr.Policy == "AuthorizeSessionPolicy") {
    var authService = context.RequestServices.GetRequiredService < IAuthorizationService > ();
    var result = await authService.AuthorizeAsync(context.User, context.GetRouteData(), authAttr.Policy);
    if (!result.Succeeded) {
     var path = $ "/Account/Login?ReturnUrl={context.Request.Path}";
     context.Response.Redirect(path);
     return;
    }
   }
  }
  await _next.Invoke(context);
 }
}

public static class CheckSessionExistMiddlewareExtensions {
 public static IApplicationBuilder UseCheckSessionExistMiddleware(this IApplicationBuilder builder) {
  return builder.UseMiddleware < CheckSessionExistMiddleware > ();
 }
}
// =======================================================================
// Step-4: Call above Middleware in Configure method of Startup.cs
// =======================================================================
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
 // ... Other Middlewares
 app.UseHttpsRedirection();
 app.UseStaticFiles();
 app.UseRouting();
 app.UseSession();
 app.UseAuthentication();
 app.UseCheckSessionExistMiddleware();
 app.UseAuthorization();
 // ... Other Middlewares
}
// SessionManager Class
//================================================================
public class SessionManager
    {
        private readonly ISession _session;
        public const string SessionKeyUserId = "_UserId";
        public SessionManager(IHttpContextAccessor httpContextAccessor)
        {
            _session = httpContextAccessor.HttpContext.Session;
        }

        public void ClearSession()
        {
            // Clear all session. FYI - there is no Abandon method like ASP.Net WebForm
            _session.Clear();
        }
        public string CurrentUserId
        {
            get
            {
                return _session.GetString(SessionKeyUserId) == null ? null : _session.GetString(SessionKeyUserId);
            }
            set
            {
                _session.SetString(SessionKeyUserId, value);
            }
        }
    }
Thats it !!