CRUD Operations Using MVC Knockout With Entity Framework

What is Knockout

Knockout is a JavaScript library that helps you to create rich, responsive displays and editor user interfaces with a clean underlying data model. Read more here http://knockoutjs.com/.

Key Concepts

  • Declarative Bindings
    Easily associate DOM elements with model data using a concise, readable syntax.

  • Automatic UI Refresh
    When your data model's state changes, your UI updates automatically

  • Dependency Tracking
    Implicitly set up chains of relationships between model data, to transform and combine it.

  • Templating
    Quickly generate sophisticated, nested UIs as a function of your model data.

Prerequisites

Visual Studio 2017 is the prerequisite to work with this article.

Thus, let's just use the sections with which we can implement the functionality.

  • Create ASP.NET MVC 5 Application.
  • Adding Model.
  • Scaffolding in MVC 5.
  • View in MVC 5.
  • Log in an Entity Framework.

Create ASP.NET MVC 5 Application

In this section, we'll create an ASP.NET Web Application with the MVC 5 Project Template. Use the procedure given below.

Step 1

Open Visual Studio 2017 and click "New Project".

Step 2

Select "Web" from the left pane and create ASP.NET Web application.

Step 3

Select the MVC Project template in the next ASP.NET wizard.

Visual Studio automatically creates the MVC 5 Application, adds some files and folders to the solution.

Working with Entity Framework

Step 1

Right click on Models folder, click Add New Item, select ADO.NET Entity Data Model from Data template and give a name.

Step 2

Select EF Designer from the database.

Step 3

Make a new connection and select a connection, if you already have a connection.

Step 4

Select tables, view, and stored procedures and click Finish.

 

Change one little thing in context class to get rid of this error “A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.Employee”.
  1. public NORTHWNDEntities(): base("name=NORTHWNDEntities") {  
  2.     base.Configuration.ProxyCreationEnabled = false;  
  3. }  
Add a new controller on controller folder and give appropriate name.

EmployeeController

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Data.Entity;  
  4. using System.Linq;  
  5. using System.Net;  
  6. using System.Web;  
  7. using System.Web.Mvc;  
  8. using System.Web.Script.Serialization;  
  9. using WebApplication1.Models;  
  10. namespace WebApplication1.Controllers {  
  11.     public class EmployeeController: Controller {  
  12.         private readonly NORTHWNDEntities _db = new NORTHWNDEntities();  
  13.         // GET: Employee  
  14.         public ActionResult Index() {  
  15.             return View();  
  16.         }  
  17.         public JsonResult ListEmployees() {  
  18.             //return Json(_db.Employees.ToList(), JsonRequestBehavior.AllowGet);  
  19.             return Json(from obj in _db.Employees select new {  
  20.                 EmployeeID = obj.EmployeeID, FirstName = obj.FirstName, LastName = obj.LastName, Address = obj.Address  
  21.             }, JsonRequestBehavior.AllowGet);  
  22.         }  
  23.         public ActionResult Create() {  
  24.             return View();  
  25.         }  
  26.         // POST: Employee/CreateEmployee  
  27.         [HttpPost]  
  28.         public string CreateEmployee(Employee employee) {  
  29.             if (!ModelState.IsValid) return "Model is invalid";  
  30.             _db.Employees.Add(employee);  
  31.             _db.SaveChanges();  
  32.             return "Cource is created";  
  33.         }  
  34.         // GET: Employee/Edit/5  
  35.         public ActionResult Edit(int ? id) {  
  36.             if (id == nullreturn new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  37.             var employee = _db.Employees.Find(id);  
  38.             if (employee == nullreturn HttpNotFound();  
  39.             var serializer = new JavaScriptSerializer();  
  40.             ViewBag.SelectedEmployee = serializer.Serialize(employee);  
  41.             return View();  
  42.         }  
  43.         // POST: Employee/Update/5  
  44.         [HttpPost]  
  45.         public string Update(Employee employee) {  
  46.             if (!ModelState.IsValid) return "Invalid model";  
  47.             _db.Entry(employee).State = EntityState.Modified;  
  48.             _db.SaveChanges();  
  49.             return "Updated successfully";  
  50.         }  
  51.         // GET: Home/Delete/5  
  52.         public ActionResult Delete(int ? id) {  
  53.             if (id == nullreturn new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  54.             var employee = _db.Employees.Find(id);  
  55.             if (employee == nullreturn HttpNotFound();  
  56.             var serializer = new JavaScriptSerializer();  
  57.             ViewBag.SelectedEmployee = serializer.Serialize(employee);  
  58.             return View();  
  59.         }  
  60.         // POST: Home/Delete/5  
  61.         [HttpPost, ActionName("Delete")]  
  62.         public string Delete(Employee employee) {  
  63.             if (employee == nullreturn "Invalid data";  
  64.             var getEmployee = _db.Employees.Find(employee.EmployeeID);  
  65.             _db.Employees.Remove(getEmployee);  
  66.             _db.SaveChanges();  
  67.             return "Deleted successfully";  
  68.         }  
  69.         protected override void Dispose(bool disposing) {  
  70.             if (disposing) {  
  71.                 _db.Dispose();  
  72.             }  
  73.             base.Dispose(disposing);  
  74.         }  
  75.     }  
  76. }  

Install Knockoutjs using Nuget Package Manager.

 

 

Image 2.

Add a new folder in Scripts folder “KOScripts” and add some JavaScript files.

KORead.js

  1. $(function () {  
  2. ko.applyBindings(modelView);  
  3. modelView.viewEmployees();  
  4. });  
  5. var modelView = {  
  6. Employees: ko.observableArray([]),  
  7. viewEmployees: function () {  
  8. var thisObj = this;  
  9. try {  
  10. $.ajax({  
  11. url: '/Employee/ListEmployees',  
  12. type: 'GET',  
  13. dataType: 'json',  
  14. contentType: 'application/json',  
  15. success: function (data) {  
  16. thisObj.Employees(data);//Here we are assigning values to KO Observable array  
  17. },  
  18. error: function (err) {  
  19. alert(err.status + " : " + err.statusText);  
  20. }  
  21. });  
  22. catch (e) {  
  23. window.location.href = '/Employee/Index';  
  24. }  
  25. },  
  26. //Create  
  27. EmployeeID: ko.observable(),  
  28. FirstName: ko.observable(),  
  29. LastName: ko.observable(),  
  30. Address: ko.observable(),  
  31. createEmployee: function () {  
  32. try {  
  33. $.ajax({  
  34. url: '/Employee/CreateEmployee',  
  35. type: 'POST',  
  36. dataType: 'json',  
  37. data: ko.toJSON(this), //Here the data wil be converted to JSON  
  38. contentType: 'application/json',  
  39. success: successCallback,  
  40. error: errorCallback  
  41. });  
  42. catch (e) {  
  43. window.location.href = '/Employee/Index';  
  44. }  
  45. }//End create  
  46. }  
  47. function successCallback(data) {  
  48. window.location.href = '/Employee/Index/';  
  49. }  
  50. function errorCallback(err) {  
  51. window.location.href = '/Employee/Index/';  
  52. }  

 

KOUpdate:

  1. var parsedSelectedEmployee = $.parseJSON(selectedEmployee);  
  2. $(function () {  
  3. ko.applyBindings(modelUpdate);  
  4. });  
  5. var modelUpdate = {  
  6. //Update  
  7. EmployeeID: ko.observable(parsedSelectedEmployee.EmployeeID),  
  8. FirstName: ko.observable(parsedSelectedEmployee.FirstName),  
  9. LastName: ko.observable(parsedSelectedEmployee.LastName),  
  10. Address: ko.observable(parsedSelectedEmployee.Address),  
  11. updateEmployee: function () {  
  12. try {  
  13. $.ajax({  
  14. url: '/Employee/Update',  
  15. type: 'POST',  
  16. dataType: 'json',  
  17. data: ko.toJSON(this),  
  18. contentType: 'application/json',  
  19. success: successCallback,  
  20. error: errorCallback  
  21. });  
  22. catch (e) {  
  23. window.location.href = '/Employee/Index/';  
  24. }  
  25. }  
  26. //End update here  
  27. }  
  28. function successCallback(data) {  
  29. window.location.href = '/Employee/Index/';  
  30. }  
  31. function errorCallback(err) {  
  32. window.location.href = '/Employee/Index/';  
  33. }  
KODelete:

 

  1. var parsedSelectedEmployee = $.parseJSON(selectedEmployee);  
  2. $(function () {  
  3. ko.applyBindings(modelDelete);  
  4. });  
  5. var modelDelete = {  
  6. //Delete  
  7. EmployeeID: ko.observable(parsedSelectedEmployee.EmployeeID),  
  8. FirstName: ko.observable(parsedSelectedEmployee.FirstName),  
  9. LastName: ko.observable(parsedSelectedEmployee.LastName),  
  10. Address: ko.observable(parsedSelectedEmployee.Address),  
  11. deleteEmployee: function () {  
  12. try {  
  13. $.ajax({  
  14. url: '/Employee/Delete',  
  15. type: 'POST',  
  16. dataType: 'json',  
  17. data: ko.toJSON(this),  
  18. contentType: 'application/json',  
  19. success: successCallback,  
  20. error: errorCallback  
  21. });  
  22. catch (e) {  
  23. window.location.href = '/Employee/Index/';  
  24. }  
  25. }  
  26. //End delete here  
  27. }  
  28. function successCallback(data) {  
  29. window.location.href = '/Employee/Index/';  
  30. }  
  31. function errorCallback(err) {  
  32. window.location.href = '/Employee/Index/';  
  33. }  
Add a new folder “Employee” in Views.

 

Index.cshtml

  1. @{  
  2. ViewBag.Title = "Index";  
  3. Layout = "~/Views/Shared/_Layout.cshtml";  
  4. }  
  5. <h2>Index</h2>  
  6. <p>  
  7. @Html.ActionLink("Create New""Create")  
  8. </p>  
  9. <table class="table">  
  10. <tr>  
  11. <th>  
  12. Employee ID  
  13. </th>  
  14. <th>  
  15. First Name  
  16. </th>  
  17. <th>  
  18. Last Name  
  19. </th>  
  20. <th>  
  21. Address  
  22. </th>  
  23. <th></th>  
  24. </tr>  
  25. <tbody data-bind="foreach: Employees">  
  26. <tr>  
  27. <td data-bind="text: EmployeeID"></td>  
  28. <td data-bind="text: FirstName"></td>  
  29. <td data-bind="text: LastName"></td>  
  30. <td data-bind="text: Address"></td>  
  31. <td>  
  32. <a data-bind="attr: { 'href': '@Url.Action("Edit", "Employee")/' + EmployeeID }" class="btn-link">Edit</a>  
  33. <a data-bind="attr: { 'href': '@Url.Action("Delete", "Employee")/' + EmployeeID }" class="btn-link">Delete</a>  
  34. </td>  
  35. </tr>  
  36. </tbody>  
  37. </table>  
  38. <script src="~/Scripts/jquery-1.10.2.min.js"></script>  
  39. <script src="~/Scripts/knockout-3.4.2.js"></script>  
  40. <script src="~/Scripts/KOScripts/KORead.js"></script>  
Create.cshtml

 

  1. @ {  
  2.     ViewBag.Title = "Create";  
  3.     Layout = "~/Views/Shared/_Layout.cshtml";  
  4. } < h2 > Create < /h2> < div class = "form-horizontal" > < h4 > Employee < /h4> < hr > < div class = "form-group" > < label class = "control-label col-md-2"  
  5. for = "EmployeeID" > Employee ID < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  6. id = "EmployeeID"  
  7. name = "EmployeeID"  
  8. type = "text"  
  9. value = ""  
  10. data - bind = "value: EmployeeID" > < /div> < /div> < div class = "form-group" > < label class = "control-label col-md-2"  
  11. for = "FirstName" > First Name < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  12. id = "FirstName"  
  13. name = "FirstName"  
  14. type = "text"  
  15. value = ""  
  16. data - bind = "value: FirstName" > < /div> < /div> < div class = "form-group" > < label class = "control-label col-md-2"  
  17. for = "LastName" > Last Name < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  18. id = "LastName"  
  19. name = "LastName"  
  20. type = "text"  
  21. value = ""  
  22. data - bind = "value: LastName" > < /div> < /div> < div class = "form-group" > < label class = "control-label col-md-2"  
  23. for = "Address" > Address < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  24. id = "Address"  
  25. name = "Address"  
  26. type = "text"  
  27. value = ""  
  28. data - bind = "value: Address" > < /div> < /div> < div class = "form-group" > < div class = "col-md-offset-2 col-md-10" > < input type = "button"  
  29. data - bind = "click: createEmployee"  
  30. value = "Create"  
  31. class = "btn btn-default" > < /div> < /div> < /div> < div > @Html.ActionLink("Back to List""") < /div> < script src = "~/Scripts/jquery-1.10.2.min.js" > < /script> < script src = "~/Scripts/knockout-3.4.2.js" > < /script> < script src = "~/Scripts/KOScripts/KORead.js" > < /script>  
Edit.cshtml
  1. @ {  
  2.     ViewBag.Title = "Edit";  
  3.     Layout = "~/Views/Shared/_Layout.cshtml";  
  4. } < h2 > Edit < /h2>  
  5. @using(Html.BeginForm()) {  
  6.         @Html.AntiForgeryToken() < div class = "form-horizontal" > < h4 > Employee < /h4> < div class = "form-group" > < label class = "control-label col-md-2"  
  7.         for = "FirstName" > First Name < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  8.         id = "FirstName"  
  9.         name = "FirstName"  
  10.         type = "text"  
  11.         value = ""  
  12.         data - bind = "value: FirstName" > < /div> < /div> < div class = "form-group" > < label class = "control-label col-md-2"  
  13.         for = "LastName" > Last Name < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  14.         id = "LastName"  
  15.         name = "LastName"  
  16.         type = "text"  
  17.         value = ""  
  18.         data - bind = "value: LastName" > < /div> < /div> < div class = "form-group" > < label class = "control-label col-md-2"  
  19.         for = "Address" > Address < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  20.         id = "Address"  
  21.         name = "Address"  
  22.         type = "text"  
  23.         value = ""  
  24.         data - bind = "value: Address" > < /div> < /div> < div class = "form-group" > < div class = "col-md-offset-2 col-md-10" > < input type = "button"  
  25.         data - bind = "click: updateEmployee"  
  26.         value = "Update"  
  27.         class = "btn btn-default" > < /div> < /div> < /div>  
  28.     } < script type = "text/javascript" >  
  29.     var selectedEmployee = '@Html.Raw(ViewBag.selectedEmployee)'; < /script> < div > @Html.ActionLink("Back to List""") < /div> < script src = "~/Scripts/jquery-1.10.2.min.js" > < /script> < script src = "~/Scripts/knockout-3.4.2.js" > < /script> < script src = "~/Scripts/KOScripts/KOUpdate.js" > < /script>  
Delete.cshtml
  1. @ {  
  2.     ViewBag.Title = "Delete";  
  3.     Layout = "~/Views/Shared/_Layout.cshtml";  
  4. } < h2 > Delete < /h2> < h3 > Are you sure you want to delete this ? < /h3>  
  5. @using(Html.BeginForm()) {  
  6.         @Html.AntiForgeryToken() < div class = "form-horizontal" > < h4 > Employee < /h4> < div class = "form-group" > < label class = "control-label col-md-2"  
  7.         for = "FirstName" > First Name < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  8.         id = "FirstName"  
  9.         name = "FirstName"  
  10.         type = "text"  
  11.         value = ""  
  12.         data - bind = "value: FirstName" > < /div> < /div> < div class = "form-group" > < label class = "control-label col-md-2"  
  13.         for = "LastName" > Last Name < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  14.         id = "LastName"  
  15.         name = "LastName"  
  16.         type = "text"  
  17.         value = ""  
  18.         data - bind = "value: LastName" > < /div> < /div> < div class = "form-group" > < label class = "control-label col-md-2"  
  19.         for = "Address" > Address < /label> < div class = "col-md-10" > < input class = "form-control text-box single-line"  
  20.         id = "Address"  
  21.         name = "Address"  
  22.         type = "text"  
  23.         value = ""  
  24.         data - bind = "value: Address" > < /div> < /div> < div class = "form-group" > < div class = "col-md-offset-2 col-md-10" > < input type = "button"  
  25.         data - bind = "click: deleteEmployee"  
  26.         value = "Delete"  
  27.         class = "btn btn-default" > < /div> < /div> < /div>  
  28.     } < script type = "text/javascript" >  
  29.     var selectedEmployee = '@Html.Raw(ViewBag.selectedEmployee)'; < /script> < div > @Html.ActionLink("Back to List""Read") < /div> < script src = "~/Scripts/jquery-1.10.2.min.js" > < /script> < script src = "~/Scripts/knockout-3.4.2.js" > < /script> < script src = "~/Scripts/KOScripts/KODelete.js" > < /script>  
Add routing URL in RouteConfig.cs for Employee.
  1. public static void RegisterRoutes(RouteCollection routes) {  
  2.     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");  
  3.     routes.MapRoute(name: "Default", url: "{controller}/{action}/{id}", defaults: new {  
  4.         controller = "Home", action = "Index", id = UrlParameter.Optional  
  5.     });  
  6.     routes.MapRoute(name: "Employee", url: "{controller}/{action}/{id}", defaults: new {  
  7.         controller = "Employee", action = "Index", id = UrlParameter.Optional  
  8.     });  
  9. }  
Now final step to add a tab in header, Go in Shared/_Layout.cshtml and add Employee tab.
  1. <div class="navbar-collapse collapse">  
  2.     <ul class="nav navbar-nav">  
  3.         <li>@Html.ActionLink("Home""Index""Home")</li>  
  4.         <li>@Html.ActionLink("About""About""Home")</li>  
  5.         <li>@Html.ActionLink("Contact""Contact""Home")</li>  
  6.         <li>@Html.ActionLink("Employee""Index""Employee")</li>  
  7.     </ul>  
  8. </div>  
Now, time to run the application to see output.

 

The index page has a list of employees, you can add new employee and edit and delete as well.

 

Now, click on any row and click Edit.

 

Click on delete link from the grid to delete any record.

 

Conclusion

In this article, we have learned how to implement CRUD operations using MVC with Knockout and Entity Framework. If you have any question or comment, drop me a line in the comments section.


Raj Kumar

Raj Kumar is 2 time Microsoft MVP and 10 time C# Corner MVP. Working as consultant with lots of hands on experience using AWS, Azure, MuleSoft, SAP, ASP.NET, C#, MVC, Angular, React, Visual Basic .NET, SQL Server, WCF, H... Read more

View All Comments