Introduction
I frequently come across questions on StackOverflow in which the questioners are able to create CRUD operations for their entity which is a normal form post, but they struggle implementing the CRUD via AJAX so that page does not reload completely for better User Experience.
In this post, I will be addressing this problem and we will learn how we can implement CRUD using jQuery DataTables in one page without any full page reloads.The end result will look something like:
I have been writing last few posts about the usage of jQuery DataTables and it can be leveraged to build a Grid View like functionality in ASP.NET MVC. We saw how we can install it using NuGet Package Manager and implement a simple Grid using the plugin which provides us the essential features like Sorting, Searching and Paginations, then we saw how we can implement the paging, filtering and ordering; then we proceeded to see how advanced search can be added for more better user experience.
If someone wants to have refresher or is interested to read the previous posts, they can refer here.
- Beginners Guide for Creating GridView in ASP.NET MVC 5
- Grid View with Server Side Filtering, Sorting and Paging in ASP.NET MVC 5
- Grid View With Server Side Advanced Search Using jQuery DataTables In ASP.NET MVC 5
In this post, we will learn how we can add create, update and delete operations support in the Grid that we already implemented using jQuery DataTables.The end result will look something like:
Database Creation Script
First of all, we will run the script for database and tables creation that would be essential for this post. Following is the script for that.
- CREATE DATABASE [AdvancedSearchGridExampleMVC]
- GO
- CREATE TABLE [dbo].[FacilitySites] ([FacilitySiteID] UNIQUEIDENTIFIER NOT NULL,
- [FacilityName] NVARCHAR (MAX) NULL,
- [IsActive] BIT NOT NULL,
- [CreatedBy] UNIQUEIDENTIFIER NOT NULL,
- [CreatedAt] DATETIME NOT NULL,
- [ModifiedBy] UNIQUEIDENTIFIER NULL,
- [ModifiedAt] DATETIME NULL,
- [IsDeleted] BIT NOT NULL
- );
- GO
- CREATE TABLE [dbo].[Assets] (
- [AssetID] UNIQUEIDENTIFIER NOT NULL,
- [Barcode] NVARCHAR (MAX) NULL,
- [SerialNumber] NVARCHAR (MAX) NULL,
- [PMGuide] NVARCHAR (MAX) NULL,
- [AstID] NVARCHAR (MAX) NOT NULL,
- [ChildAsset] NVARCHAR (MAX) NULL,
- [GeneralAssetDescription] NVARCHAR (MAX) NULL,
- [SecondaryAssetDescription] NVARCHAR (MAX) NULL,
- [Quantity] INT NOT NULL,
- [Manufacturer] NVARCHAR (MAX) NULL,
- [ModelNumber] NVARCHAR (MAX) NULL,
- [Building] NVARCHAR (MAX) NULL,
- [Floor] NVARCHAR (MAX) NULL,
- [Corridor] NVARCHAR (MAX) NULL,
- [RoomNo] NVARCHAR (MAX) NULL,
- [MERNo] NVARCHAR (MAX) NULL,
- [EquipSystem] NVARCHAR (MAX) NULL,
- [Comments] NVARCHAR (MAX) NULL,
- [Issued] BIT NOT NULL,
- [FacilitySiteID] UNIQUEIDENTIFIER NOT NULL
- );
- GO
- CREATE NONCLUSTERED INDEX [IX_FacilitySiteID]
- ON [dbo].[Assets]([FacilitySiteID] ASC);
- GO
- ALTER TABLE [dbo].[Assets]
- ADD CONSTRAINT [PK_dbo.Assets] PRIMARY KEY CLUSTERED ([AssetID] ASC);
- GO
- ALTER TABLE [dbo].[Assets]
- ADD CONSTRAINT [FK_dbo.Assets_dbo.FacilitySites_FacilitySiteID] FOREIGN KEY ([FacilitySiteID]) _
- REFERENCES [dbo].[FacilitySites] ([FacilitySiteID]) ON DELETE CASCADE;
- GO
You can find the script in attached source code in file name dbScript.sql which also contains sample data to get started quickly. Running it will create the DB and will add some sample data in the tables as well.
Create/Insert OperationFor the insertion part, first of all, we need to create a Partial View in Views >> Asset by right clicking the Asset folder and navigate to Add >> MVC 5 Partial Page (Razor) like the below screenshot.

Open the Container View in which asset rows are being rendered which _AssetsPartial.cshtml located in Views >> Asset directory and add the HTML for the Add Asset button which will open up a popup for inserting a new asset row in database,
- <button type="button" class="btn btn-default btn-md" data-toggle="modal" data-url="@Url.Action("Create","Asset")" id="btnCreateAsset">
- <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> Add Asset
- </button>
Now, open the Index View of Asset which is Views >> Asset >> index.csthtml and in scripts section add the following jQuery code for the button click event which we just created above, it will call the create get action and will display the partial view in bootstrap modal to user, the code for which is,
- $("#btnCreateAsset").on("click", function () {
- var url = $(this).data("url");
- $.get(url, function (data) {
- $('#createAssetContainer').html(data);
- $('#createAssetModal').modal('show');
- });
- });
We also need to add bootstrap modal with container div in which we will be loading create partial view of Asset, so we will add the following html at the end of Index.cshtml view,
- <div class="modal fade" id="createAssetModal" tabindex="-1" role="dialog" aria-labelledby="CreateAssetModal" aria-hidden="true" data-backdrop="static">
- <div id="createAssetContainer">
- </div>
- </div>
Add action method for Asset Creation get request in the Asset controller file located at Controllers >> AssetController.cs which would be called by the above jQuery code, inside the action method we are populating the ViewModel and passing the instance back to PartialView as normally we do in mvc based application,
- // GET: Asset/Create
- public ActionResult Create()
- {
- var model = new AssetViewModel();
- model.FacilitySitesSelectList = GetFacilitiySitesSelectList();
- return View("_CreatePartial", model);
- }
Add Partial View Creation
Add a new Partial view in project in Asset Views for Create form, for that right click the Asset folder under Views in the Solution Explorer

Enter the name of partial view to be _CreatePartial in textbox or whatever name you think would be better in your case.

Writing Add Partial View Code
Now we will write the partial view that we created in previous step _CreatePatial, add the following code in that View,
- @model GridAjaxCRUDMVC.Models.AssetViewModel
- @{
- Layout = null;
- }
- <div class="modal-dialog">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
- <h4 class="modal-title">Add Asset</h4>
- </div>
- @using (Ajax.BeginForm("Create", "Asset", null, new AjaxOptions { HttpMethod = "Post", OnSuccess = "CreateAssetSuccess" }, new { @class = "form-horizontal", role = "form" }))
- {
- <div class="modal-body">
- <div class="form-horizontal">
- @Html.ValidationSummary(true, "", new { @class = "text-danger" })
- @Html.HiddenFor(model => model.AssetID, new { Value = Guid.NewGuid() })
- <div class="form-group">
- @Html.LabelFor(model => model.Barcode, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Barcode, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Barcode, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.SerialNumber, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.SerialNumber, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.SerialNumber, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.FacilitySiteID, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.DropDownListFor(model => model.FacilitySiteID, Model.FacilitySitesSelectList, "Select One", new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.FacilitySiteID, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.PMGuide, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.PMGuide, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.PMGuide, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.AstID, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.AstID, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.AstID, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.ChildAsset, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.ChildAsset, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.ChildAsset, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.GeneralAssetDescription, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.GeneralAssetDescription, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.GeneralAssetDescription, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.SecondaryAssetDescription, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.SecondaryAssetDescription, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.SecondaryAssetDescription, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.Quantity, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Quantity, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Quantity, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.Manufacturer, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Manufacturer, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Manufacturer, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.ModelNumber, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.ModelNumber, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.ModelNumber, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.Building, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Building, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Building, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.Floor, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Floor, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Floor, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.Corridor, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Corridor, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Corridor, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.RoomNo, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.RoomNo, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.RoomNo, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.MERNo, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.MERNo, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.MERNo, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.EquipSystem, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.EquipSystem, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.EquipSystem, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.Comments, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Comments, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Comments, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.Issued, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- <div class="checkbox">
- @Html.EditorFor(model => model.Issued)
- @Html.ValidationMessageFor(model => model.Issued, "", new { @class = "text-danger" })
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="modal-footer">
- <div class="form-group">
- <div class="col-md-offset-2 col-md-10">
- <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
- <input type="submit" class="btn btn-primary" value="Save" />
- </div>
- </div>
- </div>
- }
- </div>
- </div>
The important thing to note in the view code is the Ajax.BeginForm helper portion as the class Ajax should be enough to understand that it will post the model back to controller action via Ajax, which means that whole will not refresh i.e. no full postback will happen in terms on asp.net web forms.
The post action is simply mapping ViewModel object to the Model and then saving it in the repository,
- // POST: Asset/Create
- [HttpPost]
- public async Task<ActionResult> Create(AssetViewModel assetVM)
- {
- if (!ModelState.IsValid)
- return View("_CreatePartial", assetVM);
- Asset asset = MaptoModel(assetVM);
- DbContext.Assets.Add(asset);
- var task = DbContext.SaveChangesAsync();
- await task;
- if (task.Exception != null)
- {
- ModelState.AddModelError("", "Unable to add the Asset");
- return View("_CreatePartial", assetVM);
- }
- return Content("success");
- }
We are trying to save the newly entered asset in the database and tracking if it is saved successfully or not and if it saves successfully we are returning a string message “success” back to client side and in the success call back of Ajax Form we will be checking if operation was successful or not to do UI changes according to that.
Implementing Ajax Callback FunctionWe can see that in BeginForm helper method parameters we are specifying JavaScript function to be called when the Ajax Form successfully returns back from server i.e. OnSuccess = "CreateAssetSuccess"
Now let’s go to the index.cshtml view of Asset and define the success callback implementation,
- function CreateAssetSuccess(data) {
- if (data != "success") {
- $('#createAssetContainer').html(data);
- return;
- }
- $('#createAssetModal').modal('hide');
- $('#createAssetContainer').html("");
- assetListVM.refresh();
- }
What we are doing here is if the operation is not successful we are updating the client side html to notify the user about the failure of creation and if the insert goes successful we are closing the modal popup and refreshing the Grid to display the up to date information.
Edit/Update Operation
Until now we should be able to run the application and successfully add new assets in the database via Ajax using the bootstrap modal which we created above, now let’s move to the update part of the Asset.
We will be adding another column in the datatable columns collection which will contain hyper link that will navigate to Edit View, but as we are using Ajax and bootstrap modal, we will be doing updates as well same way, so no redirect will be involved. Let’s get started.
Open the Index.csthml file and add a new column in the columns collection in jQuery datatable initialization, after updating the columns array our code would look like,
- "columns": [
- { "title": "Bar Code", "data": "BarCode", "searchable": true },
- { "title": "Manufacturer", "data": "Manufacturer", "searchable": true },
- { "title": "Model", "data": "ModelNumber", "searchable": true },
- { "title": "Building", "data": "Building", "searchable": true },
- { "title": "Room No", "data": "RoomNo" },
- { "title": "Quantity", "data": "Quantity" },
- {
- "title": "Actions",
- "data": "AssetID",
- "searchable": false,
- "sortable": false,
- "render": function (data, type, full, meta) {
- return '<a href="@Url.Action("Edit","Asset")?id=' + data + '" class="editAsset">Edit</a>';
- }
- }
- ]
We are setting the header of new column to display Actions as title and we would need to disable the searching and sorting for this column, as it is for edit operation and it does not makes sense to enable sorting and searching on this column. Next we are defining the render method of the column and we are generating anchor link html which could call the Edit action of Asset controller and will pass the current asset id to pull the information of it and display in the Edit View.
Defining Edit/Update GET ActionAfter doing the datatable js changes, now we need to create a get action method which will pull the asset record from the database and will display it for editing to the user in a popup.
Let’s implement the Edit get action of it,
- public ActionResult Edit(Guid id)
- {
- var asset = DbContext.Assets.FirstOrDefault(x => x.AssetID == id);
- AssetViewModel assetViewModel = MapToViewModel(asset);
- if (Request.IsAjaxRequest())
- return PartialView("_EditPartial",assetViewModel);
- return View(assetViewModel);
- }
The action simply retrieves the row from the database and after converting it to ViewModel passes it to back to partial view to rendered or returned back to the client side for processing, as no post backs would happen it will generate the html and will send the html back in response of ajax call which client side will handle and decide where to put that html.
Handling Action Links Events on Client Side using JQueryFrom the column render function you can see that there is class applied on anchor link called EditAsset, it is defined because jQuery event handler will be applied to the anchor link and Ajax call will be sent to server, let’s define the event handler for that in Index View:
- $('#assets-data-table').on("click", ".editAsset", function (event) {
- event.preventDefault();
- var url = $(this).attr("href");
- $.get(url, function (data) {
- $('#editAssetContainer').html(data);
- $('#editAssetModal').modal('show');
- });
- });
Now add the bootstrap modal html in the Index View which will be placeholder for loading the Edit View, define it just after create bootstrap modal html,
- <div class="modal fade" id="editAssetModal" tabindex="-1" role="dialog" aria-labelledby="EditAssetModal" aria-hidden="true" data-backdrop="static">
- <div id="editAssetContainer">
- </div>
- </div>
Edit/Update Partial View Creation
Create another new partial view following the same steps which we did for adding _CreatePartial.cshtml which was for Create Asset, So add a new Partial View in the Asset folder in Views with name _EditPartial.cshtml and add the following code in it,

Mohammad obaydullahPosted Oct 3, 2022, 10:49 AM
I can not access the database. Please help me.
turbo dlerPosted Jun 20, 2019, 7:49 AM
I would have a small question for you, what's the advantage of creating multiple modal wrappers instead of reusing the same one every time as you already load modal's content dynamically? Couldn't you simply have one <div class = "modal fade"....> rendered in the _Layout file and reuse that ?
First LastPosted Sep 13, 2018, 3:19 PM
If you try to add an empty asset - entereing nothing and clicking the Save button, I get an error: "The ViewData item that has the key 'FacilitySiteID' is of type 'System.Guid' but must be of type 'IEnumerable<SelectListItem>'."
First LastPosted Sep 13, 2018, 3:17 PM
For the section: Delete Partial View Addition, having the Layout = "~/Views/Shared/_Layout.cshtml"; there causes this partial view to NOT show.
First LastPosted Sep 13, 2018, 3:15 PM
For the section: Edit/Update Partial View Creation, @model TA_UM.ViewModels.AssetViewModel should be: @model TA_UM.Models.AssetViewModel
First LastPosted Sep 13, 2018, 1:29 PM
For the section: Create/Insert Operation, you have us create a partial view. What is the purpose of it, what should it be named and where is it used?
First LastPosted Sep 13, 2018, 1:16 PM
The MapToModel method is not defined.
First LastPosted Sep 13, 2018, 1:16 PM
The MapToViewModel method is not defined.
First LastPosted Sep 13, 2018, 1:07 PM
The GetFacilitiySitesSelectList method is not defined.
First LastPosted Sep 13, 2018, 1:04 PM
The AssetViewModel is not defined in the Models folder. It is used threw out. How should it be defined?
LawrencePosted Apr 4, 2018, 10:18 AM
Thanks for the tutorial Ehsan. As an example, if I want to display the Facility Site in a DataTable, it shows the GUID instead of the name. How does one solve this? I have spent hours trying to resolve the issue with no luck :(
rajesh paliwalPosted Mar 16, 2018, 5:33 AM
Thanks Ehsan but can you explain this example with mvc architecture with query based method i do not want to use entity framework for this. i need without entity framework can you help me??
shervin salimianPosted Feb 12, 2018, 12:35 AM
Thank you very much ehsan sajjad.i want to put image field and store in database with your project but i don't know how can i do it.i want upload image in folder an store address of image in database.can you help me plz?
Oyedepo OlufemiPosted Jun 15, 2017, 7:29 PM
Nice article! thumbs up but can u upload the full source code?
Dharmraj ThakurPosted Jun 13, 2017, 12:35 AM
Thans for sharing,,,,,,,,,,,,,,,,,,,,,,,,,,
Sundaram SubramanianPosted Jun 12, 2017, 4:26 AM
can you show me an example of CRUD in single View. means in a single page. the controls should be same. no pop up.
Sundaram SubramanianPosted Jun 12, 2017, 3:39 AM
The one which i was suffering for. excellent. Thanks bro.
Shams RehmanPosted Jun 12, 2017, 3:12 AM
I have done the same thing with Kendo UI. Ajax its too lengthy and so much line of code.By the way bro how much experience do you have in MVC??
Satyaprakash SamantarayPosted Jun 12, 2017, 1:59 AM
Where is the output??