In this part of the tutorial, you'll make some improvements to the automatically generated Details
and Delete
methods. These changes aren't required, but with just a few small bits of code, you can easily enhance the application.
Improving the Details and Delete Methods
When you scaffolded the Movie
controller, ASP.NET MVC generated code that worked great, but that can be made more robust with just a few small changes.
Open the Movie
controller and modify the Details
method by returning HttpNotFound
when a movie isn't found. You should also modify the Details
method to set a default value for the ID that's passed to it. (You made similar changes to the Edit
method in part 6 of this tutorial.) However, you must change the return type of the Details
method from ViewResult
to ActionResult
, because the HttpNotFound
method doesn't return a ViewResult
object. The following example shows the modified Details
method.
public ActionResult Details(int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null) { return HttpNotFound(); } return View(movie); }
Code First makes it easy to search for data using the Find
method. An important security feature that we built into the method is that the code verifies that the Find
method has found a movie before the code 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:xxxx/Movies/Details/1 to something like http://localhost:xxxx/Movies/Details/12345 (or some other value that doesn't represent an actual movie). If you don't the check for a null movie, this could result in a database error.
Similarly, change the Delete
and DeleteConfirmed
methods to specify a default value for the ID parameter and to return HttpNotFound
when a movie isn't found. The updated Delete
methods in the Movie
controller are shown below.
// GET: /Movies/Delete/5 public ActionResult Delete(int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null) { return HttpNotFound(); } return View(movie); } // // POST: /Movies/Delete/5 [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null) { return HttpNotFound(); } db.Movies.Remove(movie); db.SaveChanges(); return RedirectToAction("Index"); }
Note that the Delete
method doesn't delete the data. Performing a delete operation in response to a GET request (or for that matter, performing an edit operation, create operation, or any other operation that changes data) opens up a security hole. For more information about this, see Stephen Walther's blog entry ASP.NET MVC Tip #46 — Don't use Delete Links because they create Security Holes.
The HttpPost
method that deletes the data is named DeleteConfirmed
to give the HTTP POST method a unique signature or name. The two method signatures are shown below:
// GET: /Movies/Delete/5 public ActionResult Delete(int id = 0) // // POST: /Movies/Delete/5 [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id = 0)
The common language runtime (CLR) requires overloaded methods to have a unique signature (same name, different list of parameters). However, here you need two Delete methods -- one for GET and one for POST -- that both require the same signature. (They both need to accept a single integer as a parameter.)
To sort this out, you can do a couple of things. One is to give the methods different names. That's what we did in he preceding example. However, this introduces a small problem: ASP.NET maps segments of a URL to action methods by name, and if you rename a method, routing normally wouldn't be able to find that method. The solution is what you see in the example, which is to add the ActionName("Delete")
attribute to the DeleteConfirmed
method. This effectively performs mapping for the routing system so that a URL that includes /Delete/ for a POST request will find the DeleteConfirmed
method.
Another way to avoid a problem with methods that have identical names and signatures is to artificially change the signature of the POST method to include an unused parameter. For example, some developers add a parameter type FormCollection
that is passed to the POST method, and then simply don't use the parameter:
public ActionResult Delete(FormCollection fcNotUsed, int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null) { return HttpNotFound(); } db.Movies.Remove(movie); db.SaveChanges(); return RedirectToAction("Index"); }
Wrapping Up
You now have a complete ASP.NET MVC application that stores data in a SQL Server Compact database. You can create, read, update, delete, and search for movies.
This basic tutorial got you started making controllers, associating them with views, and passing around hard-coded data. Then you created and designed a data model. Entity Framework Code First created a database from the data model on the fly, and the ASP.NET MVC scaffolding system automatically generated the action methods and views for basic CRUD operations. You then added a search form that let users search the database. You changed the database to include a new column of data, and then updated two pages to create and display this new data. You added validation by marking the data model with attributes from the DataAnnotations
namespace. The resulting validation runs on the client and on the server.
If you'd like to deploy your application, it's helpful to first test the application on your local IIS 7 server. You can use this Web Platform Installer link to enable IIS setting for ASP.NET applications. See the following deployment links:
I now encourage you to move on to our intermediate-level Creating an Entity Framework Data Model for an ASP.NET MVC Application and MVC Music Store tutorials, to explore the ASP.NET articles on MSDN, and to check out the many videos and resources at http://asp.net/mvc to learn even more about ASP.NET MVC! The ASP.NET MVC forums are a great place to ask questions.
Enjoy!
— Scott Hanselman (http://hanselman.com and @shanselman on Twitter) and Rick Anderson blogs.msdn.com/rickAndy