Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Продолжим работу с проектом из прошлой темы. И изменим представление Index.cshtml, добавив в него ссылки:
@model IEnumerable<EFDataApp.Models.User> @{ ViewBag.Title = "Все пользователи"; } <a asp-action="Create">Добавить пользователя</a> <table class="table"> @foreach (var item in Model) { <tr><td>@item.Name</td> <td> <a asp-action="Details" asp-controller="Home" asp-route-id="@item.Id">Подробнее</a> | <a asp-action="Edit" asp-controller="Home" asp-route-id="@item.Id">Изменить</a> | <a asp-action="Delete" asp-controller="Home" asp-route-id="@item.Id">Удалить</a> </td></tr> } </table>
Теперь создадим для каждого действия методы.
Добавим в контроллер следующий метод:
public async Task<IActionResult> Details(int? id) { if (id != null) { User user = await db.Users.FirstOrDefaultAsync(p => p.Id == id); if (user != null) return View(user); } return NotFound(); }
Метод получает id объекта, о котором надо вывести информацию. Затем найденный объект передается в представление. Теперь добавим в папку Views/Home новое представление Details.cshtml:
@model EFDataApp.Models.User @{ ViewBag.Title = Model.Name; } <h2>Пользователь @Model.Name</h2> <div> <dl class="dl-horizontal"> <dt>Идентификатор</dt> <dd>@Model.Id</dd> <dt>Возраст</dt> <dd>@Model.Age</dd> </dl> </div>
Для редактирования объекта добавим в контроллер следующую пару методов:
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 EFDataApp.Models.User @{ ViewBag.Title = "Редактирование пользователя"; } <form asp-action="edit" asp-controller="home" asp-route-id="@Model.Id"> <div class="form-group"> <label asp-for="Name" class="control-label">Имя</label> <input type="text" asp-for="Name" class="form-control" /> </div> <div class="form-group"> <label asp-for="Age" class="control-label">Возраст</label> <input type="number" asp-for="Age" class="form-control" /> </div> <div class="form-group"> <input type="submit" value="Сохранить" class="btn btn-default" /> </div> </form>
Эта форма аналогична форме в представлении для создания нового объекта за исключением атрибута asp-route-id="@Model.Id"
.
Установка этого атрибута позволяет при отправке данных вместе с ними также отправить и значение Model.Id. Без этого значения
Entity Framework не сможет обновить соответствующую модель в базе данных.
Для создания инфраструктуры удаления добавим в контроллер следующие методы:
[HttpGet] [ActionName("Delete")] public async Task<IActionResult> ConfirmDelete(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> 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, и из-за этого мы не можем назвать методы одинаково, как в случае с Edit или Create.
И поэтому первый метод назван ConfirmDelete. Однако используемый над ним атрибут ActionName("Delete")
указывает, что
этот метод также относится к действию Delete, и поэтому мы к нему можем обращаться с запросом Home/Delete, а не Home/ConfirmDelete.
В первом методе удаляемый объект просто извлекается из БД и передается в представление. Во втором методе опять же по id получаем удаляемый объект
и удаляем его с помощью метода db.Users.Remove()
. Данный метод генерирует sql-выражение DELETE
, которое выполняется вызовом
db.SaveChangesAsync()
.
И затем добавим в папку Views/Home новое представление Delete.cshtml:
@model EFDataApp.Models.User @{ ViewBag.Title = "Удаление пользователя"; } <div> <dl class="dl-horizontal"> <dt>Имя</dt> <dd> @Html.DisplayFor(model => model.Name) </dd> <dt>Возраст</dt> <dd> @Html.DisplayFor(model => model.Age) </dd> </dl> <div> <form asp-controller="Home" asp-action="Delete" method="post"> <div class="form-group"> <input type="submit" class="btn btn-default" value="Удалить" /> </div> </form> </div> </div>
Причем надо отметит, что на форме нет никакого поля для параметра id и в теге формы также не устанавливается параметр id, как, например, в представлении Edit, так как данный параметр берется из строки запроса.
Может возникнуть вопрос, почему бы не сделать удаление сразу в методе ConfirmDelete? Дело в том, что использование get-методов не безопасно. Например, нам могут прислать письмо с картинкой:
<img src="http://адрес_нашего_сайта/Home/Delete/1" />
И при открытии письма на сервер будет отправлен get-запрос к методу ConfirmDelete, и объект с id=1 будет удален из базы данных.
И еще надо отметить, что в 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
.