Продолжим работу с проектом из прошлой темы и добавим в него возможность редактирования и удаления данных. И прежде всего изменим представление Index.cshtml, добавив в него ссылки:
@model IEnumerable<MvcApp.Models.User> <h2>Список пользователей</h2> <p><a asp-action="Create">Добавить пользователя</a></p> <table class="table"> <tr><th>Имя</th><th>Возраст</th><th></th><th></th></tr> @foreach (var item in Model) { <tr> <td>@item.Name</td> <td>@item.Age</td> <td> <a asp-action="Edit" asp-controller="Home" asp-route-id="@item.Id">Изменить</a> </td> <td> <form asp-controller="Home" asp-action="Delete" method="post" asp-route-id="@item.Id"> <input type="submit" value="Удалить" /> </form> </td> </tr> } </table>
В таблице кроме вывода имени и возраста каждого пользователя также предусмотрена ссылка на редактирование данных, а также форма с кнопкой на удаление данных. Теперь создадим для каждого действия методы.
Для создания инфраструктуры удаления добавим в контроллер следующий метод:
[HttpPost] public async Task<IActionResult> Delete(int? id) { if (id != null) { User? user = await db.Users.FirstOrDefaultAsync(p => p.Id == id); if (user != null) { db.Users.Remove(user); await db.SaveChangesAsync(); return RedirectToAction("Index"); } } return NotFound(); }
Метод принимает параметр id, с помощью которого получаем удаляемый объект из БД и, если он существует, удаляем его с помощью метода db.Users.Remove()
.
Данный метод генерирует sql-выражение DELETE
, которое выполняется вызовом db.SaveChangesAsync()
.
Стоит отметить, что данный метод Delete обрабатывает только запросы типа POST. Почему? Дело в том, что использование get-методов не безопасно. Например, нам могут прислать письмо с картинкой:
<img src="http://адрес_нашего_сайта/Home/Delete/1" />
И при открытии письма на сервер будет отправлен get-запрос. И если бы метод Delete обрабатывал бы get-запросы, то объект с id=1 был бы удален из базы данных.
Именно поэтому данный метод обрабатывает запросы POST, а для обращения к этому методу в представлении определена не просто ссылка, а форма с кнопкой, по нажатию на которую выполняется post-запрос.
И еще надо отметить, что в post-методе Delete мы можем произвести небольшую оптимизацию. Иногда бывает важно узнать перед удалением, а есть ли такой объект в БД. Однако в данном случае мы получаем два запроса к бд - один на получение объекта и второй на его удаление. И мы можем оптимизировать метод следующим образом:
[HttpPost] public async Task<IActionResult> Delete(int? id) { if (id != null) { User user = new User { Id = id.Value }; db.Entry(user).State = EntityState.Deleted; await db.SaveChangesAsync(); return RedirectToAction("Index"); } return NotFound(); }
В данном случае нам важен только id объекта. Поэтому создаем новый объект user и устанавливаем у него состояние Deleted:
db.Entry(user).State = EntityState.Deleted;
. Это выражение опять же сгенерирует sql-выражение DELETE
.
Для редактирования объекта добавим в контроллер следующую пару методов:
public async Task<IActionResult> Edit(int? id) { if(id!=null) { User? user = await db.Users.FirstOrDefaultAsync(p=>p.Id==id); if (user != null) return View(user); } return NotFound(); } [HttpPost] public async Task<IActionResult> Edit(User user) { db.Users.Update(user); await db.SaveChangesAsync(); return RedirectToAction("Index"); }
GET-версия метода Edit возвращает форму с данными объекта, которые пользователь может отредактировать. А post-версия Edit получает отредактированные
данные в виде объекта user и с помощью метода db.Users.Update(user)
для этих данных будет генерироваться
sql-выражение UPDATE
, которое будет выполнено вызовом db.SaveChangesAsync()
И также добавим для этих методов в папку Views/Home представление Edit.cshtml:
@model MvcApp.Models.User <h2>Редактирование пользователя</h2> <form asp-action="edit" asp-controller="home" asp-route-id="@Model.Id"> <p> <label asp-for="Name">Имя</label><br /> <input type="text" asp-for="Name" /> </p> <p> <label asp-for="Age">Возраст</label><br /> <input type="number" asp-for="Age" /> </p> <p> <input type="submit" value="Сохранить" /> </p> </form>
Эта форма аналогична форме в представлении для создания нового объекта за исключением атрибута asp-route-id="@Model.Id"
.
Установка этого атрибута позволяет при отправке данных вместе с ними также отправить и значение Model.Id. Без этого значения
Entity Framework не сможет обновить соответствующую модель в базе данных.