Основные операции с моделями. Редактирование и удаление

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 09.12.2019

Продолжим работу с проектом из прошлой темы. И изменим представление 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.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850