How To Write Simple Todo CRUD ASP.NET MVC Application

This article includes a lab exercise for you to demonstrate what have you learned from this training material to create your own Employee CRUD operation using EmployeeRepository

ASP.NET

Download color pdf version of this article with pictures.

Note - If you need to copy and paste the code, download the pdf file locally.

Introduction

In this session, we will learn the following.

  • How to consume Todo in-memory database using TodoRepository.
  • How to create custom ASP.NET MVC custom controller with CRUD operations.
  • List of Objects
  • Create a new insert object via View
  • Update an object via Edit View
  • Delete an Item via Delete View.
  • Write HttpPost Create API method.
  • Write Httppost Edit API method.
  • Write HttpPost Delete API method.
  • SmartIT DebugTraceHelper tool
  • A lab exercise for you to demonstrate what have you learned from this training material to create your own Employee CRUD operation using EmployeeRepository included in this training material.

Download the source code from GitHub.

MVC

UI - User Interface

We will have 5 Views,

  • Index
  • Create
  • Edit
  • Detail
  • Delete

Controller

TodoController

Wire the Controller to Database and Views.

Database

We will be using in-memory Todo repository to simplify the database.

ASP.NET                        

Create a new solution and name it TodoSolution.

ASP.NET

Add a new .NET Core Console application called "DatabaseTest".

ASP.NET

Install the SmartIT.DebugTraceHelper package's latest version from nuget.org and install the SmartIT.Employee.MockDB 1.0.4 package's latest version too.

Let's test from MockDB test to get the Todo list.

ASP.NET

  1. using SmartIT.DebugHelper;  
  2. using SmartIT.Employee.MockDB;  
  3. using System;  
  4.   
  5. namespace DatabaseTest  
  6. {  
  7.   class Program  
  8.   {  
  9.     static void Main(string[] args)  
  10.     {  
  11.       TodoRepository _todoRepository = new TodoRepository();  
  12.       var todoList=_todoRepository.GetAll();  
  13.       todoList.CDump("Todos");  
  14.       Console.ReadLine();  
  15.     }  
  16.   }  
  17. }  
You will see that SmartIT.DebugHelper object extension CDump() method writes Todo list as JSON format to the console below.

ASP.NET

Note

SmartIT.DebugHelper also has DDump() object extension method writing the Output debug window.
  1. namespace SmartIT.DebugHelper  
  2. {  
  3.   public static class DebugHelper  
  4.   {  
  5.     public static void CDump(this object obj, string message);  
  6.     public static void DDump(this object obj, string message);  
  7.   }  
  8. }  
  9. class Program  
  10.   {  
  11.     static void Main(string[] args)  
  12.     {  
  13.       TodoRepository _todoRepository = new TodoRepository();  
  14.       var todoList=_todoRepository.GetAll();  
  15.       todoList.DDump("Todos");  
  16.   
  17.       Console.ReadLine();  
  18.     }  
  19.   }  
ASP.NET

SmartIT.DebugHelper DDump() extension method is useful when you are using other project types as given below because other projects may not have console output functionally.

ASP.NET

In Database CRUD operation, we need to use the below TodoRepository functionalities.  

  1. _todoRepository.GetAll();        
  2.     _todoRepository.FindById()  
  3.     _todoRepository.Add()  
  4.      _todoRepository.Add(new SmartIT.Employee.MockDB.Todo() { Name = collection["Name"] });  
  5.     _todoRepository.Update(newTodo);  
  6.     _todoRepository.Delete(deleteTodo)  

Let's test these functionalities before we use them.

  1. static void Main(string[] args)  
  2.     {  
  3.       TodoRepository _todoRepository = new TodoRepository();  
  4.       var todoList = _todoRepository.GetAll();  
  5.       todoList.CDump("_todoRepository.GetAll()");  
  6.       var findById = _todoRepository.FindById(2);  
  7.       findById.CDump("_todoRepository.FindById(2)");  
  8.       var newTodo = _todoRepository.Add(new Todo { Name = "Call a friend" });  
  9.       _todoRepository.GetAll().CDump("Check if Call a friend todo added?");  
  10.       newTodo.Name = newTodo.Name + " Updated";  
  11.       _todoRepository.Update(newTodo);  
  12.       _todoRepository.GetAll().CDump("Check if Call a friend todo updated with Updated?");  
  13.       _todoRepository.Delete(_todoRepository.FindById(1));  
  14.       _todoRepository.GetAll().CDump("Check if Id=1 todo is Deleted?");  
We can see from the below output that all Todo functionality was passed for GetAll, FindById, Update, and Delete.

ASP.NET

Part 2

Add a new Todo.Mvc.Ui ASP.NET Core web application project.

ASP.NET

ASP.NET

Add nuGet packages to newly created Todo.Mvc.Ui project. Install the latest versions of SmartIT.DebugTraceHelper and SmartIT.Employee.MockDB 1.0.4 packages from nuget.org.

ASP.NET

ASP.NET

Rebuild the Todo.Mvc.Ui project.

Right-click on the Controllers directory and select "Add Controller".

ASP.NET

Select MVC Controller with read/write actions like below.

ASP.NET

Name the Controller as "TodoController".

ASP.NET

Add the namespaces to the TodoController file.

  1. using SmartIT.DebugHelper;  
  2. using SmartIT.Employee.MockDB;   

Add todo repository on the top of the TodoController class as a private member.

TodoRepository _todoRepository = new TodoRepository();

Update the public ActionResult Index() method return value with all todo list calling GetAll() method.

return View(_todoRepository.GetAll());

Here, you can see the changes in the below code section.

  1. public class TodoController : Controller  
  2.   {  
  3.     TodoRepository _todoRepository = new TodoRepository();  
  4.     // GET: Todo  
  5.     public ActionResult Index()  
  6.     {  
  7.   
  8.       return View(_todoRepository.GetAll());  
  9.     }   

Right-click on the View in Index() method and select "Add View" like in the below screenshot.

ASP.NET

Choose default View name as "Index".

Our Todo class is not in Model class drop-down list. So, we need a workaround.

ASP.NET

Work around

Inside Models directory, add a new Workaround class.

ASP.NET

And temporarily, write a new Todo class that inherits from SmartIT.Employee.MockDB.Todo class.

  1. namespace Todo.Mvc.Ui.Models  
  2. {  
  3.   public class Todo : SmartIT.Employee.MockDB.Todo{}  
  4. }   

Now, add a View to Index and chose Todo model class.

ASP.NET

Index.html page changes the model namespace to SmartIT.Employee.MockDB.

Let's change the Route tempale from template: "{controller=Home}/{action=Index}/{id?}"); to todo.

  1. app.UseMvc(routes =>  
  2.             {  
  3.                 routes.MapRoute(  
  4.                     name: "default",  
  5.                     template: "{controller=Todo}/{action=Index}/{id?}");  
  6.             });  

And, run the project.

From Index page, click the "Create New" link.

ASP.NET

Update the HttpPost Create method like below.
  1. [HttpPost]  
  2.     [ValidateAntiForgeryToken]  
  3.     public ActionResult Create(IFormCollection collection)  
  4.     {  
  5.       try  
  6.       {  
  7.         _todoRepository.Add(new SmartIT.Employee.MockDB.Todo() { Name=collection["Name"});  
  8.   
  9.         return RedirectToAction(nameof(Index));  
  10.       }  
  11.       catch  
  12.       {  
  13.         return View();  
  14.       }  
  15.     }  

Add a new todo.

ASP.NET

ASP.NET

  1. @model IEnumerable<SmartIT.Employee.MockDB.Todo>  
  2. @{  
  3.   ViewData["Title"] = "Index";  
  4. }  
  5. <h2>Index</h2>  
  6. <p>  
  7.   <a asp-action="Create">Create New</a>  
  8. </p>  
  9. <table class="table">  
  10.   <thead>  
  11.     <tr>  
  12.       <th>  
  13.         @Html.DisplayNameFor(model => model.Id)  
  14.       </th>  
  15.       <th>  
  16.         @Html.DisplayNameFor(model => model.Name)  
  17.       </th>  
  18.       <th></th>  
  19.     </tr>  
  20.   </thead>  
  21.   <tbody>  
  22.     @foreach (var item in Model)  
  23.     {  
  24.       <tr>  
  25.         <td>  
  26.           @Html.DisplayFor(modelItem => item.Id)  
  27.         </td>  
  28.         <td>  
  29.           @Html.DisplayFor(modelItem => item.Name)  
  30.         </td>  
  31.         <td>  
  32.           @Html.ActionLink("Edit""Edit"new { /* id=item.PrimaryKey */ }) |  
  33.           @Html.ActionLink("Details""Details"new { /* id=item.PrimaryKey */ }) |  
  34.           @Html.ActionLink("Delete""Delete"new { /* id=item.PrimaryKey */ })  
  35.         </td>  
  36.       </tr>  
  37.     }  
  38.   </tbody>  
  39. </table>   

Press F5 and run the project. Then, go to todo like below.

http://localhost:63274/todo

ASP.NET

Add a "Create page". Go to TodoController Create() Method, right click on the method, and select "Add View".

ASP.NET

Select View name and View Model like in the below picture and click the "Add" button.

ASP.NET

Inside Create.html, change the first line from Todo.Mvc.Ui.Models.Todo to SmartIT.Employee.MockDB.Todo. Delete the id section.

  1. <div class="form-group">  
  2.                 <label asp-for="Id" class="control-label"></label>  
  3.                 <input asp-for="Id" class="form-control" />  
  4.                 <span asp-validation-for="Id" class="text-danger"></span>  
  5.  </div>   

We delete the id because it is automatically added by the TodoRepository.

  1. @model SmartIT.Employee.MockDB.Todo  
  2.   
  3. @{  
  4.     ViewData["Title"] = "Create";  
  5. }  
  6.   
  7. <h2>Create</h2>  
  8.   
  9. <h4>Todo</h4>  
  10. <hr />  
  11. <div class="row">  
  12.     <div class="col-md-4">  
  13.         <form asp-action="Create">  
  14.             <div asp-validation-summary="ModelOnly" class="text-danger"></div>  
  15.               <div class="form-group">  
  16.                 <label asp-for="Name" class="control-label"></label>  
  17.                 <input asp-for="Name" class="form-control" />  
  18.                 <span asp-validation-for="Name" class="text-danger"></span>  
  19.             </div>  
  20.             <div class="form-group">  
  21.                 <input type="submit" value="Create" class="btn btn-default" />  
  22.             </div>  
  23.         </form>  
  24.     </div>  
  25. </div>  
  26.   
  27. <div>  
  28.     <a asp-action="Index">Back to List</a>  
  29. </div>  
  30.   
  31. @section Scripts {  
  32.     @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}  
  33. }   

Add an "Edit" View page.

Go to the Index.cshtml page.

  1. <td>  
  2.           @Html.ActionLink("Edit""Edit"new { /* id=item.PrimaryKey */ }) |  
  3.           @Html.ActionLink("Details""Details"new { /* id=item.PrimaryKey */ }) |  
  4.           @Html.ActionLink("Delete""Delete"new { /* id=item.PrimaryKey */ })  
  5.  </td>   

Update the Edit, Details, and Delete links.

  1. @Html.ActionLink("Edit""Edit"new { id=item.Id }) |  
  2. @Html.ActionLink("Details""Details"new { id=item.Id }) |  
  3. @Html.ActionLink("Delete""Delete"new { id=item.Id })  

Go to the TodoController page and update the public ActionResult Edit(int id) method, and pass the findTodo to the View.

  1. // GET: Todo/Edit/5  
  2.     public ActionResult Edit(int id)  
  3.     {  
  4.       var findTodo = _todoRepository.FindById(id);  
  5.       return View(findTodo);  
  6.     }  

Add a new "Edit" View to the Edit method like below. The template is Edit and Model class is Todo.

Click "Add" button.

ASP.NET

This will create the Edit.cshtml page like below.

  1. @model Todo.Mvc.Ui.Models.Todo  
  2. @{  
  3.   ViewData["Title"] = "Edit";  
  4. }  
  5. <h2>Edit</h2>  
  6. <h4>Todo</h4>  
  7. <hr />  
  8. <div class="row">  
  9.   <div class="col-md-4">  
  10.     <form asp-action="Edit">  
  11.       <div asp-validation-summary="ModelOnly" class="text-danger"></div>  
  12.       <div class="form-group">  
  13.         <label asp-for="Id" class="control-label"></label>  
  14.         <input asp-for="Id" class="form-control" />  
  15.         <span asp-validation-for="Id" class="text-danger"></span>  
  16.       </div>  
  17.       <div class="form-group">  
  18.         <label asp-for="Name" class="control-label"></label>  
  19.         <input asp-for="Name" class="form-control" />  
  20.         <span asp-validation-for="Name" class="text-danger"></span>  
  21.       </div>  
  22.       <div class="form-group">  
  23.         <input type="submit" value="Save" class="btn btn-default" />  
  24.       </div>  
  25.     </form>  
  26.   </div>  
  27. </div>  
  28. <div>  
  29.   <a asp-action="Index">Back to List</a>  
  30. </div>  
  31. @section Scripts {  
  32.   @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}  
  33. }  

Delete the Id section because we don’t want to update the primary key Id.

  1. <div class="form-group">  
  2.         <label asp-for="Id" class="control-label"></label>  
  3.         <input asp-for="Id" class="form-control" />  
  4.         <span asp-validation-for="Id" class="text-danger"></span>  
  5.  </div>   

Update Todo reference from @model Todo.Mvc.Ui.Models.Todo to SmartIT.Employee.MockDB.Todo

Below is the final updated version of Edit View.

  1. @model SmartIT.Employee.MockDB.Todo  
  2. @{  
  3.   ViewData["Title"] = "Edit";  
  4. }  
  5. <h2>Edit</h2>  
  6. <h4>Todo</h4>  
  7. <hr />  
  8. <div class="row">  
  9.   <div class="col-md-4">  
  10.     <form asp-action="Edit">  
  11.       <div asp-validation-summary="ModelOnly" class="text-danger"></div>  
  12.       <div class="form-group">  
  13.         <label asp-for="Name" class="control-label"></label>  
  14.         <input asp-for="Name" class="form-control" />  
  15.         <span asp-validation-for="Name" class="text-danger"></span>  
  16.       </div>  
  17.       <div class="form-group">  
  18.         <input type="submit" value="Save" class="btn btn-default" />  
  19.       </div>  
  20.     </form>  
  21.   </div>  
  22. </div>  
  23. <div>  
  24.   <a asp-action="Index">Back to List</a>  
  25. </div>  
  26. @section Scripts {  
  27.   @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}  
  28. }   

Press F5 and run the project. From the Index page, click "Edit".

ASP.NET

Let's wire the Edit/Save button.

Go back to TodoController HttpPost Edit method and update the // TODO: Add update logic here section.

  1. [HttpPost]  
  2.     [ValidateAntiForgeryToken]  
  3.     public ActionResult Edit(int id, IFormCollection collection)  
  4.     {  
  5.       try  
  6.       {  
  7.         // TODO: Add update logic here  
  8.   
  9.         return RedirectToAction(nameof(Index));  
  10.       }  
  11.       catch  
  12.       {  
  13.         return View();  
  14.       }  
  15.     }  

Here is the updated edit section. Using id, we find the todo item and using form collection with name key, findTodo.Name = collection["Name"];

We can update the Name value. 

  1. [HttpPost]  
  2.     [ValidateAntiForgeryToken]  
  3.     public ActionResult Edit(int id, IFormCollection collection)  
  4.     {  
  5.       try  
  6.       {  
  7.         var findTodo = _todoRepository.FindById(id);  
  8.         findTodo.Name = collection["Name"];  
  9.   
  10.         return RedirectToAction(nameof(Index));  
  11.       }  
  12.       catch  
  13.       {  
  14.         return View();  
  15.       }  
  16.     }   

Press F5 and run the project. From the Index page, click "Edit".

Click on the Id=2 Do Dishes Edit link.

Update name to "Do dishes-DONE".

ASP.NET

Click "Save" button and we can see the value is updated in the Index View.

ASP.NET

Let's create the Details View.

Go to TodoController Details method below.

Add a View named as Details, select the template as Details, and set the Model class as Todo.

ASP.NET

Click "Add" button and Detail.cshtml will be created like below.

Detail.cshtml

  1. @model Todo.Mvc.Ui.Models.Todo  
  2. @{  
  3.     ViewData["Title"] = "Details";  
  4. }  
  5. <h2>Details</h2>  
  6. <div>  
  7.     <h4>Todo</h4>  
  8.     <hr />  
  9.     <dl class="dl-horizontal">  
  10.         <dt>  
  11.             @Html.DisplayNameFor(model => model.Id)  
  12.         </dt>  
  13.         <dd>  
  14.             @Html.DisplayFor(model => model.Id)  
  15.         </dd>  
  16.         <dt>  
  17.             @Html.DisplayNameFor(model => model.Name)  
  18.         </dt>  
  19.         <dd>  
  20.             @Html.DisplayFor(model => model.Name)  
  21.         </dd>  
  22.     </dl>  
  23. </div>  
  24. <div>  
  25.     @Html.ActionLink("Edit""Edit"new { /* id = Model.PrimaryKey */ }) |  
  26.     <a asp-action="Index">Back to List</a>  
  27. </div> 

As usual, update your workaround change from Todo.Mvc.Ui.Models.Todo name space to SmartIT.Employee.MockDB.Todo.

Update Edit link id from   @Html.ActionLink("Edit", "Edit", new { /* id = Model.PrimaryKey */ })  to new { id = Model.Id })

Here is the updated Details View.

  1. @model SmartIT.Employee.MockDB.Todo  
  2. @{  
  3.     ViewData["Title"] = "Details";  
  4. }  
  5. <h2>Details</h2>  
  6. <div>  
  7.     <h4>Todo</h4>  
  8.     <hr />  
  9.     <dl class="dl-horizontal">  
  10.         <dt>  
  11.             @Html.DisplayNameFor(model => model.Id)  
  12.         </dt>  
  13.         <dd>  
  14.             @Html.DisplayFor(model => model.Id)  
  15.         </dd>  
  16.         <dt>  
  17.             @Html.DisplayNameFor(model => model.Name)  
  18.         </dt>  
  19.         <dd>  
  20.             @Html.DisplayFor(model => model.Name)  
  21.         </dd>  
  22.     </dl>  
  23. </div>  
  24. <div>  
  25.     @Html.ActionLink("Edit""Edit"new { id = Model.Id }) |  
  26.     <a asp-action="Index">Back to List</a>  
  27. </div> 

Let's update the Details method that returns the selected Todo item to the Details view.

  1. // GET: Todo/Details/5  
  2. public ActionResult Details(int id)  
  3. {  
  4.   return View();  
  5. }   

Using the id Details method parameter find the Todo item and sent to the View.

Here is the updated Details method.

  1. public ActionResult Details(int id)  
  2. {  
  3.   var findTodo = _todoRepository.FindById(id);  
  4.   return View(findTodo);  

Press F5 and run the project.

Pick "Call Boss" Todo item and see the Detail View like below.

ASP.NET

Add Delete View.

Go to TodoController and Update the Delete method from  

  1. // GET: Todo/Delete/5  
  2. public ActionResult Delete(int id)  
  3. {  
  4.   return View();  
  5. }   

To find delete Todo item using passed id number, pass to the View as argument like below.

  1. public ActionResult Delete(int id)  
  2. {  
  3.   return View(_todoRepository.FindById(id));  
  4. }   

Add "Delete" View like below by selecting View name as Delete, Template as Delete, and Model class as Todo.

ASP.NET

Click the "Add" button.

Using out workaround update Todo name space from @model Todo.Mvc.Ui.Models.Todo to SmartIT.Employee.MockDB.Todo.

Here is the updated Delete.cshtml View page.

  1. @model SmartIT.Employee.MockDB.Todo  
  2. @{  
  3.   ViewData["Title"] = "Delete";  
  4. }  
  5. <h2>Delete</h2>  
  6. <h3>Are you sure you want to delete this?</h3>  
  7. <div>  
  8.   <h4>Todo</h4>  
  9.   <hr />  
  10.   <dl class="dl-horizontal">  
  11.     <dt>  
  12.       @Html.DisplayNameFor(model => model.Id)  
  13.     </dt>  
  14.     <dd>  
  15.       @Html.DisplayFor(model => model.Id)  
  16.     </dd>  
  17.     <dt>  
  18.       @Html.DisplayNameFor(model => model.Name)  
  19.     </dt>  
  20.     <dd>  
  21.       @Html.DisplayFor(model => model.Name)  
  22.     </dd>  
  23.   </dl>  
  24.   
  25.   <form asp-action="Delete">  
  26.     <input type="submit" value="Delete" class="btn btn-default" /> |  
  27.     <a asp-action="Index">Back to List</a>  
  28.   </form>  
  29. </div>   

Update the HttpPost Delete method section // TODO: Add delete logic here 

  1. // POST: Todo/Delete/5  
  2.     [HttpPost]  
  3.     [ValidateAntiForgeryToken]  
  4.     public ActionResult Delete(int id, IFormCollection collection)  
  5.     {  
  6.       try  
  7.       {  
  8.         // TODO: Add delete logic here  
  9.   
  10.         return RedirectToAction(nameof(Index));  
  11.       }  
  12.       catch  
  13.       {  
  14.         return View();  
  15.       }  
  16.     }  

With the following code.

  1. public ActionResult Delete(int id, IFormCollection collection)  
  2.     {  
  3.       try  
  4.       {  
  5.         var findTodo = _todoRepository.FindById(id);  
  6.         _todoRepository.Delete(findTodo);  
  7.   
  8.         return RedirectToAction(nameof(Index));  
  9.       }  
  10.       catch  
  11.       {  
  12.         return View();  
  13.       }  
  14.     }  

Press F5 and run the project again.

Select Do Dishes link and below, the Delete View shows.

ASP.NET

Click on the "Delete" button.

We can see that "Do Dishes" Todo item has been deleted.

ASP.NET

Finally, comment out the workaround class which we don’t need anymore.

Summary

In this article, we have learned,

  • How to consume Todo in-memory database using TodoRepository
  • How to create custom ASP.NET MVC custom controller with CRUD operations.
  • List of Objects
  • How to create a new insert object via View
  • How to update an object via Edit View
  • How to delete an Item via Delete View.
  • How to write HttpPost and create API method.
  • How to write Httppost and edit API method.
  • How to write HttpPost and delete API method.
  • About the SmartIT DebugTraceHelper tool

Download source code from GitHub.

Lab Exercise

A lab exercise for you to demonstrate what have you learned from this training material  - Create your own Employee CRUD operation using EmployeeRepository is included in this training material.

You can follow the above steps to create your own Employee CRUD ASP.NET MVC application.

  1.  //Use below Employee repository to created your CRUD operation  
  2.   EmployeeRepository _employeeRepository= new EmployeeRepository();  
  3.   _employeeRepository.Add(new Employee() { Name = "Mat Stone", Gender = "Male", DepartmentId = 2, Salary = 8000 });  
  4.   _employeeRepository.CDump("Employees");  
  5. //  DebugHelper.cs(29): Employees  
  6. //{ "Items":[{"Id":1,"Name":"Mike","Gender":"Male","Salary":8000,"DepartmentId":1,"Department":null},  
  7. //{"Id":2,"Name":"Adam","Gender":"Male","Salary":5000,"DepartmentId":1,"Department":null},  
  8. //{"Id":3,"Name":"Jacky","Gender":"Female","Salary":9000,"DepartmentId":1,"Department":null},  
  9. //{"Id":4,"Name":"Mat Stone","Gender":"Male","Salary":8000,"DepartmentId":2,"Department":null}],"Count":4}