Edit & Delete actions

Implementation of Edit and Delete

Let us see the implementation of Edit and Delete functionalities for the movies list, on the same lines. We need to scaffold these like we did it for other action methods:

Contents of Edit.cshtml file

@model MvcMovie.Models.Movie

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

<h1>Edit</h1>

<h4>Movie</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="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

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

Contents of Delete.cshtml file

@model MvcMovie.Models.Movie

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

<h1>Delete</h1>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Id)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Id)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
    
    <form asp-action="Delete">
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-action="Index">Back to List</a>
    </form>
</div>

Edit action method in the controller (GET)

// GET: MoviesController/Edit/5
public ActionResult Edit(int? id)
{
    if (id == null || _context.Movie == null)
    {
        return NotFound();
    }

    var movie = _context.Movie.Single(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

Edit action method in the controller (POST)

// POST: MoviesController/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        _context.Update(movie);
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

Delete action method in the controller

// GET: MoviesController/Delete/5
public ActionResult Delete(int id)
{
    if (id == null || _context.Movie == null)
    {
        return NotFound();
    }

    var movie =  _context.Movie.Single(m => m.Id == id);

    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

An important security feature built into the method is that the code verifies that the search method has found a movie before it tries to do anything with it

For example, a hacker could introduce errors into the site by changing the URL created by the links from http://localhost:{PORT}/Movies/Details/1 to something likehttp://localhost:{PORT}/Movies/Details/12345

DeleteConfirmed action method in the controller

Note that the HTTP GET Delete method doesn't delete the specified movie, it returns a view of the movie where you can submit (HttpPost) the deletion.

Performing a delete operation in response to a GET request (or for that matter, performing any operation that changes data) opens up a security hole!

// POST: Movies/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
    if (_context.Movie == null)
    {
        return Problem("Movie data is null!");
    }
    var movie =  _context.Movie.Single(m=>m.Id == id);

    if (movie != null)
    {
        _context.Movie.Remove(movie);
    }            
    return RedirectToAction(nameof(Index));
}

The ActionName("Delete") attribute performs mapping for the routing system so that a URL that includes /Delete/ for a POST request will find the DeleteConfirmed method! Another common work around for methods that have identical names and signatures is to artificially change the signature of the POST method to include an extra (unused) parameter!

Add Update method stub in FakeContent

public void Update(Movie movie)
{
    //todo: implement later
    return;
}

The application should compile and run successfully now; however, it will not be fully functional since we do not have a real data store backing our context!

Notes

  • We do model validation wherever applicable

  • We redirect to the index page using the RedirectToAction method

  • We can do exception handling where applicable, in order to provide a better user experience

Last updated