Introducing ASP.NET Web Pages - Deleting Database Data
This tutorial shows you how to delete an individual database entry. It assumes you have completed the series through Updating Database Data in ASP.NET Web Pages.
What you’ll learn:
- How to select an individual record from a listing of records.
- How to delete a single record from a database.
- How to check that a specific button was clicked in a form.
Features/technologies discussed:
- The
WebGrid
helper.- The SQL
Delete
command.- The
Database.Execute
method to run a SQLDelete
command.
What You’ll Build
In the previous tutorial, you learned how to update an existing database record. This tutorial is similar, except that instead of updating the record, you’ll delete it. The processes are much the same, except that deleting is simpler, so this tutorial will be short.
In the Movies page, you’ll update the WebGrid
helper so that it displays a Delete link next to each movie to accompany the Edit link you added earlier.
As with editing, when you click the Delete link, it takes you to a different page, where the movie information is already in a form:
You can then click the button to delete the record permanently.
Adding a Delete Link to the Movie Listing
You’ll start by adding a Delete link to the WebGrid
helper. This link is similar to the Edit link you added in a previous tutorial.
Open the Movies.cshtml file.
Change the WebGrid
markup in the body of the page by adding a column. Here’s the modified markup:
[!code-htmlMain]
1: @grid.GetHtml(
2: tableStyle: "grid",
3: headerStyle: "head",
4: alternatingRowStyle: "alt",
5: columns: grid.Columns(
6: grid.Column(format: @<a href="~/EditMovie?id=@item.ID">Edit</a>),
7: grid.Column("Title"),
8: grid.Column("Genre"),
9: grid.Column("Year"),
10: grid.Column(format: @<a href="~/DeleteMovie?id=@item.ID">Delete</a>)
11: )
12: )
The new column is this one:
[!code-htmlMain]
1: grid.Column(format: @<a href="~/DeleteMovie?id=@item.ID">Delete</a>)
The way the grid is configured, the Edit column is leftmost in the grid and the Delete column is rightmost. (There’s a comma after the Year
column now, in case you didn’t notice that.) There’s nothing special about where these link columns go, and you could as easily put them next to each other. In this case, they’re separate to make them harder to get mixed up.
The new column shows a link (<a>
element) whose text says “Delete”. The target of the link (its href
attribute) is code that ultimately resolves to something like this URL, with the id
value different for each movie:
[!code-cssMain]
1: http://localhost:43097/DeleteMovie?id=7
This link will invoke a page named DeleteMovie and pass it the ID of the movie you’ve selected.
This tutorial won’t go into detail about how this link is constructed, because it’s almost identical to the Edit link from the previous tutorial (Updating Database Data in ASP.NET Web Pages).
Creating the Delete Page
Now you can create the page that will be the target for the Delete link in the grid.
[!NOTE]
Important The technique of first selecting a record to delete and then using a separate page and button to confirm the process is extremely important for security. As you’ve read in previous tutorials, making any sort of change to your website should always be done using a form — that is, using an HTTP POST operation. If you made it possible to change the site just by clicking a link (that is, using a GET operation), people could make simple requests to your site and delete your data. Even a search-engine crawler that’s indexing your site could inadvertently delete data just by following links.
When your app lets people change a record, you have to present the record to the user for editing anyway. But you might be tempted to skip this step for deleting a record. Don’t skip that step, though. (It’s also helpful for users to see the record and confirm that they’re deleting the record that they intended.)
In a subsequent tutorial set, you’ll see how to add login functionality so a user would have to log in before deleting a record.
Create a page named DeleteMovie.cshtml and replace what’s in the file with the following markup:
[!code-cshtmlMain]
1: <html>
2: <head>
3: <title>Delete a Movie</title>
4: </head>
5: <body>
6: <h1>Delete a Movie</h1>
7: @Html.ValidationSummary()
8: <p><a href="~/Movies">Return to movie listing</a></p>
9:
10: <form method="post">
11: <fieldset>
12: <legend>Movie Information</legend>
13:
14: <p><span>Title:</span>
15: <span>@title</span></p>
16:
17: <p><span>Genre:</span>
18: <span>@genre</span></p>
19:
20: <p><span>Year:</span>
21: <span>@year</span></p>
22:
23: <input type="hidden" name="movieid" value="@movieId" />
24: <p><input type="submit" name="buttonDelete" value="Delete Movie" /></p>
25: </fieldset>
26: </form>
27: </body>
28: </html>
This markup is like the EditMovie pages, except that instead of using text boxes (<input type="text">
), the markup includes <span>
elements. There’s nothing here to edit. All you have to do is display the movie details so that users can make sure that they’re deleting the right movie.
The markup already contains a link that lets the user return to the movie listing page.
As in the EditMovie page, the ID of the selected movie is stored in a hidden field. (It’s passed into the page in the first place as a query string value.) There’s an Html.ValidationSummary
call that will display validation errors. In this case, the error might be that no movie ID was passed to the page or that the movie ID is invalid. This situation could occur if someone ran this page without first selecting a movie in the Movies page.
The button caption is Delete Movie, and its name attribute is set to buttonDelete
. The name
attribute will be used in the code to identify the button that submitted the form.
You’ll have to write code to 1) read the movie details when the page is first displayed and 2) actually delete the movie when the user clicks the button.
Adding Code to Read a Single Movie
At the top of the DeleteMovie.cshtml page, add the following code block:
[!code-cshtmlMain]
1: @{
2: var title = "";
3: var genre = "";
4: var year = "";
5: var movieId = "";
6:
7: if(!IsPost){
8: if(!Request.QueryString["ID"].IsEmpty() && Request.QueryString["ID"].IsInt()){
9: movieId = Request.QueryString["ID"];
10: var db = Database.Open("WebPagesMovies");
11: var dbCommand = "SELECT * FROM Movies WHERE ID = @0";
12: var row = db.QuerySingle(dbCommand, movieId);
13: if(row != null) {
14: title = row.Title;
15: genre = row.Genre;
16: year = row.Year;
17: }
18: else{
19: Validation.AddFormError("No movie was found for that ID.");
20: }
21: }
22: else{
23: Validation.AddFormError("No movie was found for that ID.");
24: }
25: }
26: }
This markup is the same as the corresponding code in the EditMovie page. It gets the movie ID out of the query string and uses the ID to read a record from the database. The code includes the validation test (IsInt()
and row != null
) to make sure that the movie ID being passed to the page is valid.
Remember that this code should only run the first time the page runs. You don’t want to re-read the movie record from the database when the user clicks the Delete Movie button. Therefore, code to read the movie is inside a test that says if(!IsPost)
— that is, if the request is not a post operation (form submission).
Adding Code to Delete the Selected Movie
To delete the movie when the user clicks the button, add the following code just inside the closing brace of the @
block:
[!code-csharpMain]
1: if(IsPost && !Request["buttonDelete"].IsEmpty()){
2: movieId = Request.Form["movieId"];
3: var db = Database.Open("WebPagesMovies");
4: var deleteCommand = "DELETE FROM Movies WHERE ID = @0";
5: db.Execute(deleteCommand, movieId);
6: Response.Redirect("~/Movies");
7: }
This code is similar to the code for updating an existing record, but simpler. The code basically runs a SQL Delete
statement.
As in the EditMovie page, the code is in an if(IsPost)
block. This time, the if()
condition is a little more complicated:
[!code-csharpMain]
1: if(IsPost && !Request["buttonDelete"].IsEmpty())
There are two conditions here. The first is that the page is being submitted, as you’ve seen before — if(IsPost)
.
The second condition is !Request["buttonDelete"].IsEmpty()
, meaning that the request has an object named buttonDelete
. Admittedly, it’s an indirect way of testing which button submitted the form. If a form contains multiple submit buttons, only the name of the button that was clicked appears in the request. Therefore, logically, if the name of a particular button appears in the request — or as stated in the code, if that button isn’t empty — that’s the button that submitted the form.
The &&
operator means “and” (logical AND). Therefore the entire if
condition is …
This request is a post (not a first-time request)
AND
The buttonDelete
button was the button that submitted the form.
This form (in fact, this page) contains only one button, so the additional test for buttonDelete
is technically not required. Still, you’re about to perform an operation that will permanently remove data. So you want to be as sure as possible that you’re performing the operation only when the user has explicitly requested it. For example, suppose that you expanded this page later and added other buttons to it. Even then, the code that deletes the movie will run only if the buttonDelete
button was clicked.
As in the EditMovie page, you get the ID from the hidden field and then run the SQL command. The syntax for the Delete
statement is:
DELETE FROM table WHERE ID = value
It’s vital to include the WHERE
clause and the ID. If you leave out the WHERE clause, all the records in the table will be deleted. As you have seen, you pass the ID value to the SQL command by using a placeholder.
Testing the Movie Delete Process
Now you can test. Run the Movies page, and click Delete next to a movie. When the DeleteMovie page appears, click Delete Movie.
When you click the button, the code deletes the movies and returns to the movie listing. There you can search for the deleted movie and confirm that it’s been deleted.
Coming Up Next
The next tutorial shows you how to give all the pages on your site a common look and layout.
Complete Listing for Movie Page (Updated with Delete Links)
[!code-cshtmlMain]
1: @{
2: var db = Database.Open("WebPagesMovies") ;
3: var selectCommand = "SELECT * FROM Movies";
4: var searchTerm = "";
5:
6: if(!Request.QueryString["searchGenre"].IsEmpty() ) {
7: selectCommand = "SELECT * FROM Movies WHERE Genre = @0";
8: searchTerm = Request.QueryString["searchGenre"];
9: }
10:
11: if(!Request.QueryString["searchTitle"].IsEmpty() ) {
12: selectCommand = "SELECT * FROM Movies WHERE Title LIKE @0";
13: searchTerm = "%" + Request.QueryString["searchTitle"] + "%";
14: }
15:
16: var selectedData = db.Query(selectCommand, searchTerm);
17: var grid = new WebGrid(source: selectedData, defaultSort: "Genre", rowsPerPage:3);
18: }
19: <!DOCTYPE html>
20: <html lang="en">
21: <head>
22: <meta charset="utf-8" />
23: <title>Movies</title>
24: <style type="text/css">
25: .grid { margin: 4px; border-collapse: collapse; width: 600px; }
26: .grid th, .grid td { border: 1px solid #C0C0C0; padding: 5px; }
27: .head { background-color: #E8E8E8; font-weight: bold; color: #FFF; }
28: .alt { background-color: #E8E8E8; color: #000; }
29: </style>
30: </head>
31: <body>
32: <h1>Movies</h1>
33: <form method="get">
34: <div>
35: <label for="searchGenre">Genre to look for:</label>
36: <input type="text" name="searchGenre" value="@Request.QueryString["searchGenre"]" />
37: <input type="Submit" value="Search Genre" /><br/>
38: (Leave blank to list all movies.)<br/>
39: </div>
40:
41: <div>
42: <label for="SearchTitle">Movie title contains the following:</label>
43: <input type="text" name="searchTitle" value="@Request.QueryString["searchTitle"]" />
44: <input type="Submit" value="Search Title" /><br/>
45: </div>
46:
47: </form>
48: <div>
49: @grid.GetHtml(
50: tableStyle: "grid",
51: headerStyle: "head",
52: alternatingRowStyle: "alt",
53: columns: grid.Columns(
54: grid.Column(format: @<a href="~/EditMovie?id=@item.ID">Edit</a>),
55: grid.Column("Title"),
56: grid.Column("Genre"),
57: grid.Column("Year"),
58: grid.Column(format: @<a href="~/DeleteMovie?id=@item.ID">Delete</a>)
59: )
60: )
61: </div>
62: <p>
63: <a href="~/AddMovie">Add a movie</a>
64: </p>
65: </body>
66: </html>
Complete Listing for DeleteMovie Page
[!code-cshtmlMain]
1: @{
2: var title = "";
3: var genre = "";
4: var year = "";
5: var movieId = "";
6:
7: if(!IsPost){
8: if(!Request.QueryString["ID"].IsEmpty() && Request.QueryString["ID"].IsInt()){
9: movieId = Request.QueryString["ID"];
10: var db = Database.Open("WebPagesMovies");
11: var dbCommand = "SELECT * FROM Movies WHERE ID = @0";
12: var row = db.QuerySingle(dbCommand, movieId);
13: if(row != null) {
14: title = row.Title;
15: genre = row.Genre;
16: year = row.Year;
17: }
18: else{
19: Validation.AddFormError("No movie was found for that ID.");
20: }
21: }
22: else{
23: Validation.AddFormError("No movie was found for that ID.");
24: }
25: }
26:
27: if(IsPost && !Request["buttonDelete"].IsEmpty()){
28: movieId = Request.Form["movieId"];
29: var db = Database.Open("WebPagesMovies");
30: var deleteCommand = "DELETE FROM Movies WHERE ID = @0";
31: db.Execute(deleteCommand, movieId);
32: Response.Redirect("~/Movies");
33: }
34: }
35: <html>
36: <head>
37: <title>Delete a Movie</title>
38: </head>
39: <body>
40: <h1>Delete a Movie</h1>
41: @Html.ValidationSummary()
42: <p><a href="~/Movies">Return to movie listing</a></p>
43:
44: <form method="post">
45: <fieldset>
46: <legend>Movie Information</legend>
47:
48: <p><span>Title:</span>
49: <span>@title</span></p>
50:
51: <p><span>Genre:</span>
52: <span>@genre</span></p>
53:
54: <p><span>Year:</span>
55: <span>@year</span></p>
56:
57: <input type="hidden" name="movieid" value="@movieId" />
58: <p><input type="submit" name="buttonDelete" value="Delete Movie" /></p>
59: </fieldset>
60: <p><a href="~/Movies">Return to movie listing</a></p>
61: </form>
62: </body>
63: </html>
Additional Resources
- Introduction to ASP.NET Web Programming by Using the Razor Syntax
- SQL DELETE Statement on the W3Schools site
|