The community is working on translating this tutorial into Chinese, but it seems that no one has started the translation process for this article yet. If you can help us, then please click "More info".
TODO List: The Controller
In the previous articles, we setup a database for our TODO List web application. We added a Model, a ViewModel and a Helper class, and even the View to visually represent the list. Now it's time to add the glue that ties it all together: The Controller.
The Controller
The TODO List web application that we're currently creating will only serve one purpose: It will be a TODO List! Therefore, we only need a single controller, called the HomeController. In a larger application, it would likely be called TodoController or TodoListController, but by calling it the HomeController, we can access the Index view (which will also be the only required View) from the root of the web app.
Our Controller will have several actions which will allow us to add new items to the TODO list, as well as editing or deleting existing items. All of these actions require their own method, which might require some explanation, so I'll just go through all the methods individually first, and then you'll get the complete Controller code listing at the end.
Index():
public IActionResult Index()
{
TodoListViewModel viewModel = new TodoListViewModel();
return View("Index", viewModel);
}
Not a lot happening here. Since our TODO List application only has a single View, this is where we deliver it. Since the View uses the TodoListViewModel, we create an instance of it here (which will load the TODO items, as described in the previous article) and then we pass it to the view using the View() method.
Edit():
public IActionResult Edit(int id)
{
TodoListViewModel viewModel = new TodoListViewModel();
viewModel.EditableItem = viewModel.TodoItems.FirstOrDefault(x => x.Id == id);
return View("Index", viewModel);
}
The Edit() action works just like the Index() action, except for the middle line, where we set the EditableItem property to the item requested by the user through the id parameter. We re-use the same View (Index), which will automatically respond to the fact that we're now passing in an existing TodoListItem.
Delete():
public IActionResult Delete(int id)
{
using(var db = DbHelper.GetConnection())
{
TodoListItem item = db.Get<TodoListItem>(id);
if(item != null)
db.Delete(item);
return RedirectToAction("Index");
}
}
The Delete() method, much like the Edit() method, takes a single parameter called id - with that parameter, we can find the requested item from the database (using the Get() method from Dapper.Contrib) and when we have it, we can call the Delete() method on it (again from Dapper.Contrib). This is a fine example of how Dapper and Dapper.Contrib makes everything easier for us - instead of having to write SQL for fetching and then deleting a row, we simply use to simple .NET methods.
CreateUpdate():
public IActionResult CreateUpdate(TodoListViewModel viewModel)
{
if(ModelState.IsValid)
{
using(var db = DbHelper.GetConnection())
{
if(viewModel.EditableItem.Id <= 0)
{
viewModel.EditableItem.AddDate = DateTime.Now;
db.Insert<TodoListItem>(viewModel.EditableItem);
}
else
{
TodoListItem dbItem = db.Get<TodoListItem>(viewModel.EditableItem.Id);
var result = TryUpdateModelAsync<TodoListItem>(dbItem, "EditableItem");
db.Update<TodoListItem>(dbItem);
}
}
return RedirectToAction("Index");
}
else
return View("Index", new TodoListViewModel());
}
The most complex method in our Controller is the CreateUpdate() method. Since the FORM we have in our View will be used for both adding new items as well as editing existing items, the method which handles the request also needs to take care of both situations.
The first thing we do, is to check the ModelState.IsValid property - this will tell us whether our Model could be validated or not, according to the model validation we added in the previous article. If it's not valid, we call the View() method and simply display the Index view again - however, the ModelState will be included and it will be used in the validation summary we display in our View, to display the validation errors generated when validating the Model.
If the Model is valid, we look at Id property of the EditableItem - if it's not higher than zero, then we're dealing with a new item. We simply assign the current DateTime to the AddDate property and then we call the Insert() method to insert our new item in the database.
If the Id is higher than zero, we're dealing with an existing item. We fetch the real item from the database and then we call the TryUpdateModelAsync() method on it. This will update the object we have from the database with values found in the Model posted to the server, which in this case is basically just the Title property. Once our model has been updated, we write the changes back to the database by calling the Update() method.
When all this is done, we simply do a redirect to the Index action, essentially returning everything to the way it was before. The changes made will automatically be reflected when the Index action is called and the ViewModel is loaded. Be aware that we don't do any error checking or exception handling during this process, to keep the example as compact as possible - you should of course, as a minimum, handle any possible exception in a real-world application.
ToggleIsDone:
public IActionResult ToggleIsDone(int id)
{
using(var db = DbHelper.GetConnection())
{
TodoListItem item = db.Get<TodoListItem>(id);
if(item != null)
{
item.IsDone = !item.IsDone;
db.Update<TodoListItem>(item);
}
return RedirectToAction("Index");
}
}
We activate this action with a small piece of JavaScript found in our View. It will simply fetch the TodoListItem specified by the id parameter, using the Get() method on the database. Once we have the the item, we change the IsDone to be False if it's currently True and vice versa. As soon as we have done that, we update the corresponding row in the database, using the Update() method.
That was the final method in our Controller. As promised, here's the full code listing:
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using TodoList.Models;
using Dapper;
using Dapper.Contrib.Extensions;
using TodoList.ViewModels;
using TodoList.Helpers;
namespace TodoList.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
TodoListViewModel viewModel = new TodoListViewModel();
return View("Index", viewModel);
}
public IActionResult Edit(int id)
{
TodoListViewModel viewModel = new TodoListViewModel();
viewModel.EditableItem = viewModel.TodoItems.FirstOrDefault(x => x.Id == id);
return View("Index", viewModel);
}
public IActionResult Delete(int id)
{
using(var db = DbHelper.GetConnection())
{
TodoListItem item = db.Get<TodoListItem>(id);
if(item != null)
db.Delete(item);
return RedirectToAction("Index");
}
}
public IActionResult CreateUpdate(TodoListViewModel viewModel)
{
if(ModelState.IsValid)
{
using(var db = DbHelper.GetConnection())
{
if(viewModel.EditableItem.Id <= 0)
{
viewModel.EditableItem.AddDate = DateTime.Now;
db.Insert<TodoListItem>(viewModel.EditableItem);
}
else
{
TodoListItem dbItem = db.Get<TodoListItem>(viewModel.EditableItem.Id);
TryUpdateModelAsync<TodoListItem>(dbItem, "EditableItem");
db.Update<TodoListItem>(dbItem);
}
}
return RedirectToAction("Index");
}
else
return View("Index", new TodoListViewModel());
}
public IActionResult ToggleIsDone(int id)
{
using(var db = DbHelper.GetConnection())
{
TodoListItem item = db.Get<TodoListItem>(id);
if(item != null)
{
item.IsDone = !item.IsDone;
db.Update<TodoListItem>(item);
}
return RedirectToAction("Index");
}
}
}
}
Summary
Congratulations, you have just built your very own database-driven TODO List web application! Now add some items to it, click on the title to edit the item, click on the checkbox to toggle the IsDone property or click the Delete button to remove an item. All changes will be reflected in the database and be visible the next time you load the page - awesome!