MVVM and Knockout Using Entity Framework in Web Application

This article explains how to use the Model View ViewModel (MVVM) pattern with Knockout using Entity Framework in ASP.Net web applications.

MVVM: The MVVM pattern solves the problem of de-coupling the View from the Model, so that Views can be implemented by the designers instead of software developers.

  • VIEW: Binds to the ViewModel using only data binding.
  • MODEL: A Model is responsible for exposing data in a way that is easily consumable by WPF. It must implement INotifyPropertyChanged and/or INotifyCollectionChanged as appropriate.
  • ViewModel: A ViewModel is a model for a view in the application or we can say as an abstraction of the view. It exposes data relevant to the view and exposes the behaviors for the views, usually with Commands.

Knockout: KnockoutJS is a pure JavaScript framework to implement the MVVM design pattern in the web application development. The main key concepts of KO is:

  • Declarative Bindings
  • Automatic UI Refresh
  • Dependency Tracking
  • Templating

Getting Started

  • Start Visual Studio
  • Create a new website
  • Provide the name and location of the website
  • Click "Next"

This is the employee table entity data diagram:

entity data diagram

This is model class:
  1. public partial class Employee  
  2. {  
  3.     public Employee()  
  4.     {  
  5.         this.Employees1 = new HashSet<Employee>();  
  6.     }  
  7.     public int EmployeeID { getset; }  
  8.     public string LastName { getset; }  
  9.     public string FirstName { getset; }  
  10.     public string Title { getset; }  
  11.     public string TitleOfCourtesy { getset; }  
  12.     public Nullable<System.DateTime> BirthDate { getset; }  
  13.     public Nullable<System.DateTime> HireDate { getset; }  
  14.     public string Address { getset; }  
  15.     public string City { getset; }  
  16.     public string Region { getset; }  
  17.     public string PostalCode { getset; }  
  18.     public string Country { getset; }  
  19.     public string HomePhone { getset; }  
  20.     public string Extension { getset; }  
  21.     public byte[] Photo { getset; }  
  22.     public string Notes { getset; }  
  23.     public string PhotoPath { getset; }  
  24.     public virtual ICollection<Employee> Employees1 { getset; }  
  25.     public virtual Employee Employee1 { getset; }  
  26. } 

This is the context class:

  1. public partial class NorthwindEntities2 : DbContext  
  2. {  
  3.     public NorthwindEntities2() : base("name=NorthwindEntities2")  
  4.     {  
  5.     }  
  6.     protected override void OnModelCreating(DbModelBuilder modelBuilder)  
  7.     {  
  8.         throw new UnintentionalCodeFirstException();  
  9.     }  
  10.     public DbSet<Employee> Employees { getset; }  
  11. } 

Add these js, assembly references.

assembly references

Now for the ViewModel.

  1. <script src="Scripts/jquery-1.10.2.js"></script>  
  2.  <script src="Scripts/knockout-3.0.0.js"></script>  
  3.  <script type="text/javascript">  
  4.      function Employee(data) {  
  5.          this.EmployeeID = ko.observable(data.EmployeeID);  
  6.          this.LastName = ko.observable(data.LastName);  
  7.          this.FirstName = ko.observable(data.FirstName);  
  8.          this.Address = ko.observable(data.Address);  
  9.          this.City = ko.observable(data.City);  
  10.          this.Region = ko.observable(data.Region);  
  11.          this.PostalCode = ko.observable(data.PostalCode);  
  12.          this.Country = ko.observable(data.Country);  
  13.          this.PhotoPath = ko.observable(data.PhotoPath);  
  14.      }  
  15.      // View Model  
  16.      function EmployeeViewModel() {  
  17.          var self = this;  
  18.          self.Countries = ko.observableArray(['USA''UK''India']);  
  19.          self.Employees = ko.observableArray([]);  
  20.          self.EmployeeID = ko.observable();  
  21.          self.LastName = ko.observable();  
  22.          self.FirstName = ko.observable();  
  23.          self.Address = ko.observable();  
  24.          self.City = ko.observable();  
  25.          self.Region = ko.observable();  
  26.          self.PostalCode = ko.observable();  
  27.          self.Country = ko.observable();  
  28.          self.PhotoPath = ko.observable();  
  29.          self.AddEmployee = function () {  
  30.              self.Employees.push(new Employee({  
  31.                  EmployeeID: self.EmployeeID(),  
  32.                  LastName: self.LastName(),  
  33.                  FirstName: self.FirstName(),  
  34.                  Address: self.Address(),  
  35.                  City: self.City(),  
  36.                  Region: self.Region(),  
  37.                  PostalCode: self.PostalCode(),  
  38.                  Country: self.Country(),  
  39.                  PhotoPath: self.PhotoPath()  
  40.              }));  
  41.              self.EmployeeID("");  
  42.              self.LastName("");  
  43.              self.FirstName("");  
  44.              self.Address("");  
  45.              self.City("");  
  46.              self.Region("");  
  47.              self.PostalCode("");  
  48.              self.Country("");  
  49.              self.PhotoPath("");  
  50.          };  
  51.          //Remove from list  
  52.          self.RemoveEmployee = function (employee) {  
  53.              self.Employees.remove(employee)  
  54.          };  
  55.          // Save data in database  
  56.          self.SaveToDb = function () {  
  57.              jQuery.ajax({  
  58.                  type: "POST",  
  59.                  url: "http://localhost:16072/Default.aspx/SaveEmployees",  
  60.                  data: ko.toJSON({ data: self.Employees }),  
  61.                  contentType: "application/json",  
  62.                  success: function (result) {  
  63.                      alert(result.d);  
  64.                  }  
  65.              });  
  66.          };  
  67.          //Delete Employee  
  68.          self.DeleteEmployee = function () {  
  69.              jQuery.ajax({  
  70.                  type: "POST",  
  71.                  url: "http://localhost:16072/Default.aspx/DeleteEmployee",  
  72.                  data: ko.toJSON({ data: self.Employees }),  
  73.                  contentType: "application/json",  
  74.                  success: function (result) {  
  75.                      alert(result.d);  
  76.                      self.Students.remove(student)  
  77.                  },  
  78.                  error: function (err) {  
  79.                      alert(err.status + " - " + err.statusText);  
  80.                  }  
  81.              });  
  82.          };  
  83.          //get EMployee  
  84.          $.ajax({  
  85.              type: "POST",  
  86.              url: 'http://localhost:16072/Default.aspx/GetEmployees',  
  87.              contentType: "application/json; charset=utf-8",  
  88.              dataType: "json",  
  89.              success: function (results) {  
  90.                  var employees = $.map(results.d, function (item) {  
  91.                      return new Employee(item)  
  92.                  });  
  93.                  self.Employees(employees);  
  94.              },  
  95.              error: function (err) {  
  96.                  alert(err.status + " - " + err.statusText);  
  97.              }  
  98.          });  
  99.      }  
  100.      //KO binding  
  101.      $(document).ready(function () {  
  102.          ko.applyBindings(new EmployeeViewModel());  
  103.      });  
  104.  </script> 

Code Behind

  1. [WebMethod]  
  2.  public static Employee[] GetEmployees()  
  3.  {  
  4.      var dbContext = new NorthwindEntities2();  
  5.      var data = (from item in dbContext.Employees  
  6.                  orderby item.EmployeeID  
  7.                  select item).Take(5);  
  8.      return data.ToArray();  
  9.  }  
  10.  [WebMethod]  
  11.  public static string SaveEmployees(Employee[] data)  
  12.  {  
  13.      var dbContext = new NorthwindEntities2();  
  14.      var employeeList = from dbEmployee in dbContext.Employees select dbEmployee;  
  15.      foreach (Employee employeeOld in data)  
  16.      {  
  17.          var employee = new Employee();  
  18.          if (employeeOld != null)  
  19.          {  
  20.              employee.EmployeeID = employeeOld.EmployeeID;  
  21.              employee.FirstName = employeeOld.FirstName;  
  22.              employee.LastName = employeeOld.LastName;  
  23.              employee.Address = employeeOld.Address;  
  24.              employee.City = employeeOld.City;  
  25.              employee.Region = employeeOld.Region;  
  26.              employee.PostalCode = employeeOld.PostalCode;  
  27.              employee.Country = employeeOld.Country;  
  28.              employee.PhotoPath = employeeOld.PhotoPath;                
  29.          }  
  30.          Employee emp = (from em in employeeList  
  31.                          where  
  32.                              em.EmployeeID == employee.EmployeeID  
  33.                          select em).FirstOrDefault();  
  34.          if (emp == null)  
  35.              dbContext.Employees.Add(employee);  
  36.          dbContext.SaveChanges();  
  37.      }  
  38.      return "Employee saved to database!";  
  39.  }  
  40.  [WebMethod]  
  41.  public static string DeleteEmployee(Employee data)  
  42.  {  
  43.      try  
  44.      {  
  45.          var dbContext = new NorthwindEntities2();  
  46.          var employee = dbContext.Employees.FirstOrDefault  
  47.          (userId => userId.EmployeeID == data.EmployeeID);  
  48.          if (employee != null)  
  49.          {  
  50.              if (employee != null)  
  51.              {  
  52.                  dbContext.Employees.Remove(employee);  
  53.                  dbContext.SaveChanges();  
  54.              }  
  55.          }  
  56.          return "Employee deleted from database!";  
  57.      }  
  58.      catch (Exception ex)  
  59.      {  
  60.          return "Error: " + ex.Message;  
  61.      }  
  62.  } 

Now to display the data:

  1. <form id="form1" runat="server">  
  2.         <h3>Add New Employee</h3>  
  3.         <table>  
  4.             <tr>  
  5.                 <td>Employee ID :</td>  
  6.                 <td><input data-bind="value: EmployeeID" /></td>  
  7.             </tr>  
  8.             <tr>  
  9.                 <td>Last Name :</td>  
  10.                 <td><input data-bind="value: LastName" /></td>  
  11.             </tr>  
  12.             <tr>  
  13.                 <td>First Name :</td>  
  14.                 <td>  
  15.                     <input data-bind="value: FirstName" /></td>  
  16.             </tr>  
  17.             <tr>  
  18.                 <td>Address :</td>  
  19.                 <td><input data-bind="value: Address" /></td>  
  20.             </tr>  
  21.             <tr>  
  22.                 <td>City :</td>  
  23.                 <td><input data-bind="value: City" /></td>  
  24.             </tr>  
  25.             <tr>  
  26.                 <td>Region :</td>  
  27.                 <td><input data-bind="value: Region" /></td>  
  28.             </tr>  
  29.             <tr>  
  30.                 <td>Postal Code :</td>  
  31.                 <td><input data-bind="value: PostalCode" /></td>  
  32.             </tr>  
  33.             <tr>  
  34.                 <td>Country :</td>  
  35.                 <td><select data-bind="options: Countries, value: Country, optionsCaption: 'Select Country...'"></select>  
  36.                 </td>  
  37.             </tr>  
  38.             <tr>  
  39.                 <td>Photo Path :</td>  
  40.                 <td><input data-bind="value: PhotoPath" /></td>  
  41.             </tr>  
  42.             <tr>  
  43.                 <td colspan="2">  
  44.                     <button type="button" data-bind="click: AddEmployee">Add Employee</button>  
  45.                     <button type="button" data-bind="click: SaveToDb">Save To Database</button>  
  46.                 </td>  
  47.             </tr>  
  48.         </table>  
  49.         <table data-bind="visible: Employees().length > 0" border="0">  
  50.             <tr>  
  51.                 <th>Employee ID</th>  
  52.                 <th>Last Name</th>  
  53.                 <th>First Name</th>  
  54.                 <th>Adress</th>  
  55.                 <th>City</th>  
  56.                 <th>Region</th>  
  57.                 <th>PostalCode</th>  
  58.                 <th>Country</th>  
  59.                 <th>Photo</th>  
  60.                 <th>Action</th>  
  61.             </tr>  
  62.             <tbody data-bind="foreach: Employees">  
  63.                 <tr>  
  64.                     <td><span data-bind="text: EmployeeID" /></td>  
  65.                     <td><input data-bind="value: LastName" /></td>  
  66.                     <td><input data-bind="value: FirstName" /></td>  
  67.                      <td><input data-bind="value: Address" /></td>  
  68.                      <td><input data-bind="value: City" /></td>  
  69.                      <td><input data-bind="value: Region" /></td>  
  70.                      <td><input data-bind="value: PostalCode" /></td>  
  71.                     <td><select data-bind="options: $root.Countries, value: Country"></select></td>  
  72.                      <td><input data-bind="value: PhotoPath" /></td>  
  73.                     <td><a href="#" data-bind="click: $root.RemoveEmployee">Remove From List</a></td>  
  74.                     <td><a href="#" data-bind="click: $root.DeleteEmployee">Delete From DataBase</a></td>  
  75.                 </tr>  
  76.             </tbody>  
  77.         </table>  
  78.     </form> 

Run sample


Run application