How To Write Simple Todo CRUD ASP.NET MVC Application

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 a 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 the Httppost Edit API method.
  • Write the 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 an in-memory Todo repository to simplify the database.

Todo repository

Create a new solution and name it TodoSolution.

 Todo Solution

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

DatabaseTest

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.

MockDB test

using SmartIT.DebugHelper;
using SmartIT.Employee.MockDB;
using System;

namespace DatabaseTest
{
    class Program
    {
        static void Main(string[] args)
        {
            TodoRepository _todoRepository = new TodoRepository();
            var todoList = _todoRepository.GetAll();
            todoList.CDump("Todos");
            Console.ReadLine();
        }
    }
}

You will see that SmartIT.DebugHelper object extension CDump() method writes the Todo list in JSON format to the console below.

JSON

Note. SmartIT.DebugHelper also has a DDump() object extension method writing the Output debug window.

namespace SmartIT.DebugHelper
{
    public static class DebugHelper
    {
        public static void CDump(this object obj, string message);
        public static void DDump(this object obj, string message);
    }
}

class Program
{
    static void Main(string[] args)
    {
        TodoRepository _todoRepository = new TodoRepository();
        var todoList=_todoRepository.GetAll();
        todoList.DDump("Todos");

        Console.ReadLine();
    }
}

Output

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.

SmartIT.DebugHelper DDump

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

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

Let's test these functionalities before we use them.

static void Main(string[] args)
{
    TodoRepository _todoRepository = new TodoRepository();
    var todoList = _todoRepository.GetAll();
    todoList.CDump("_todoRepository.GetAll()");
    var findById = _todoRepository.FindById(2);
    findById.CDump("_todoRepository.FindById(2)");
    var newTodo = _todoRepository.Add(new Todo { Name = "Call a friend" });
    _todoRepository.GetAll().CDump("Check if Call a friend todo added?");
    newTodo.Name = newTodo.Name + " Updated";
    _todoRepository.Update(newTodo);
    _todoRepository.GetAll().CDump("Check if Call a friend todo updated with Updated?");
    _todoRepository.Delete(_todoRepository.FindById(1));
    _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.

 Todo functionality

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

 Todo.Mvc.Ui ASP.NET

Model view controller

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

MockDB 1.0.4 packages

Solution

Rebuild the Todo.Mvc.Ui project.

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

Add Controller

Select MVC Controller with read/write actions like below.

MVC Controller

Name the Controller as "TodoController".

Todo Controller

Add the namespaces to the TodoController file.

using SmartIT.DebugHelper;
using SmartIT.Employee.MockDB;

Add the 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 lists calling the GetAll() method.

return View(_todoRepository.GetAll());

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

public class TodoController : Controller
{
    TodoRepository _todoRepository = new TodoRepository();
    
    // GET: Todo
    public ActionResult Index()
    {
        return View(_todoRepository.GetAll());
    }
}

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

Add View

Choose the default View name as "Index".

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

Model class drop-down list

Work around

Inside the Models directory, add a new Workaround class.

 Models directory

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

namespace Todo.Mvc.Ui.Models
{
    public class Todo : SmartIT.Employee.MockDB.Todo { }
}

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

Todo model class

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

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

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

And, run the project.

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

Create New Todo

Update the HttpPost Create method like below.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(IFormCollection collection)
{
    try
    {
        _todoRepository.Add(new SmartIT.Employee.MockDB.Todo() { Name = collection["Name"] });

        return RedirectToAction(nameof(Index));
    }
    catch
    {
        return View();
    }
}

Add a new todo.

New todo

Create Index

@model IEnumerable<SmartIT.Employee.MockDB.Todo>
@{
    ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Id)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Id)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
                    @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
                    @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
                </td>
            </tr>
        }
    </tbody>
</table>

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

http://localhost:63274/todo

ASP.NET Localhost

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

Create page

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

Add

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

<div class="form-group">
    <label asp-for="Id" class="control-label"></label>
    <input asp-for="Id" class="form-control" />
    <span asp-validation-for="Id" class="text-danger"></span>
</div>

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

@model SmartIT.Employee.MockDB.Todo

@{
    ViewData["Title"] = "Create";
}

<h2>Create</h2>

<h4>Todo</h4>
<hr />

<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

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

Add an "Edit" View page.

Go to the Index.cshtml page.

<td>
    @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
    @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
    @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>

Update the Edit, Details, and Delete links.

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

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

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

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

Click the "Add" button.

Edit method

This will create the Edit.cshtml page like below.

@model Todo.Mvc.Ui.Models.Todo
@{
    ViewData["Title"] = "Edit";
}

<h2>Edit</h2>
<h4>Todo</h4>
<hr />

<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

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

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

<div class="form-group">
    <label asp-for="Id" class="control-label"></label>
    <input asp-for="Id" class="form-control" />
    <span asp-validation-for="Id" class="text-danger"></span>
</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.

@model SmartIT.Employee.MockDB.Todo
@{
    ViewData["Title"] = "Edit";
}

<h2>Edit</h2>
<h4>Todo</h4>
<hr />

<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

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

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

Edit Todo

Let's wire the Edit/Save button.

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

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, IFormCollection collection)
{
    try
    {
        // TODO: Add update logic here

        return RedirectToAction(nameof(Index));
    }
    catch
    {
        return View();
    }
}

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.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, IFormCollection collection)
{
    try
    {
        var findTodo = _todoRepository.FindById(id);
        findTodo.Name = collection["Name"];

        return RedirectToAction(nameof(Index));
    }
    catch
    {
        return View();
    }
}

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

Click on the Id=2 Do Dishes Edit link.

Update the name to "Do dishes-DONE".

Do dishes-DONE

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

Save

Let's create the Details View.

Go to the TodoController Details method below.

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

 Model class as Todo

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

Detail. cshtml

@model Todo.Mvc.Ui.Models.Todo
@{
    ViewData["Title"] = "Details";
}

<h2>Details</h2>
<div>
    <h4>Todo</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Id)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Id)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Name)
        </dd>
    </dl>
</div>
<div>
    @Html.ActionLink("Edit", "Edit", new { /* id = Model.PrimaryKey */ }) |
    <a asp-action="Index">Back to List</a>
</div>

As usual, update your workaround change from Todo. Mvc. Ui.Models.Todo namespace 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.

@model SmartIT.Employee.MockDB.Todo
@{
    ViewData["Title"] = "Details";
}

<h2>Details</h2>
<div>
    <h4>Todo</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Id)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Id)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Name)
        </dd>
    </dl>
</div>
<div>
    @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) |
    <a asp-action="Index">Back to List</a>
</div>

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

// GET: Todo/Details/5
public ActionResult Details(int id)
{
    return View();
}

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

Here is the updated Details method.

public ActionResult Details(int id)
{
    var findTodo = _todoRepository.FindById(id);
    return View(findTodo);
}

Press F5 and run the project.

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

Call Boss

Add Delete View.

Go to TodoController and Update the Delete method from  

// GET: Todo/Delete/5
public ActionResult Delete(int id)
{
    return View();
}

To find deleted Todo items using the passed ID number, pass it to the View as an argument like below.

public ActionResult Delete(int id)
{
    return View(_todoRepository.FindById(id));
}

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

Delete

Click the "Add" button.

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

Here is the updated Delete.cshtml View page.

@model SmartIT.Employee.MockDB.Todo
@{
    ViewData["Title"] = "Delete";
}

<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Todo</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Id)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Id)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Name)
        </dd>
    </dl>

    <form asp-action="Delete">
        <input type="submit" value="Delete" class="btn btn-default" /> |
        <a asp-action="Index">Back to List</a>
    </form>
</div>

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

// POST: Todo/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id, IFormCollection collection)
{
    try
    {
        // TODO: Add delete logic here

        return RedirectToAction(nameof(Index));
    }
    catch
    {
        return View();
    }
}

With the following code.

public ActionResult Delete(int id, IFormCollection collection)
{
    try
    {
        var findTodo = _todoRepository.FindById(id);
        _todoRepository.Delete(findTodo);

        return RedirectToAction(nameof(Index));
    }
    catch
    {
        return View();
    }
}

Press F5 and run the project again.

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

Do Dishes link

Click on the "Delete" button.

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

Todo item deleted

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 a 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.

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


Similar Articles