14 Days Free Technical Video Training from WintellectNOW

  

ASP.NET MVC ActionResults explained

Tags: ASP.NET, ASP.NET MVC, Razor

Action results are an important part of the ASP.NET MVC controller system, and definitely worth taking a good look at. Understanding how they work gives you many more choices in MVC and that will certainly help make your code better.

What is an ActionResult?

An ActionResult is a return type of a controller method, also called an action method, and serves as the base class for *Result classes. Action methods return models to views, file streams, redirect to other controllers, or whatever is necessary for the task at hand. The controller takes on this responsibility to connect system components, acting as a traffic cop.

There are many derived ActionResult types you can use to return results that are more specific for a particular view. You can quickly access the derived types of the ActionResult during development by hovering over an ActionResult in an action result's method signature then expanding the type tool window, to see what *Results the ASP.NET Framework provides.

When expanded, the Derived Types tab of tool window displays a list of result types derived from ActionResult. Although the Derived Types tab is the option we're focusing on now, you may also want to briefly inspect the other browsing options - Contains, References, Returned By and Base Types.

To get a deeper look at what exactly an ActionResult type is and how it works, running the code with a breakpoint set at the end of an action result method will get the information we want. Inspecting the image below of the Watch window shows some of the properties that you can tap into that ActionResults return.

Notice the Model, TempData, ViewBag, and ViewData properties. Drilling into the Watch window even further exposes the Model property's strongly typed collection of objects.

SNAGHTML4d9b785_thumb3

These objects contain everything you need when working in the view. Of course, the Model is the most heavily used object, containing our application's schema and data. The TempData object holds data between multiple controllers, while ViewBag and ViewData objects holds data between controllers and/or views.

Since the Model property is the actual model the view uses to render the data, its data type should align with the type specified in the @model directive in the target view. As pointed out in the image above, the Model property contains a strongly typed list of Product objects (or whatever you're returning), matching the view's model.

Looking in the Watch window shows that the runtime converts the ActionResult to a ViewResult, however you'll want to be more specific during design time. Swapping ActionResults for more specific/derived classes, for example, a ViewResult, produce the exact same object structure as the ActionResult. With this in mind, using more specific types increases code accuracy, maintainability, and even performance as the compiler has a much better idea of what should happen at compile time, rather than at the last minute at runtime, since no runtime conversion happens.

Action methods, action results & routing

Action methods and the routing system work together in every MVC application. MVC does this by using a set of conventions, defined as routing patterns in the global.asax.cs file. These patterns connect HTTP requests with controllers and action methods. As an HTTP Request enters the routing system, it determines which controller should serve data to which view (if any).

The global.asax.cs defines the default routing pattern, {controller}/{action}/{id}, as described in more detail here:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
    routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );
}

Picking apart each piece of the route pattern is a great way to digest what the routing system is doing...

  • {controller} Controller maps a part of the URL to the name of the controller class. For example, a class named ProductsController, will map to URLs that start with /Products (after the domain of course). ASP.NET automatically drops the word "Controller" from the URL so is it's more user friendly, e.g., /Products vs. /ProductsController.
  • {action} Action maps a part of the URL to the individual methods inside controllers. Therefore the ProductsController.Edit(...) and ProductsController.Details(...) methods align with the /Products/Edit and Products/Details URLs, respectively. Action maps without any parameters, since the next route pattern, id, defines them.
  • {id} Id maps to an identifier, such as a product id or a product name. These are controller action method parameters, so the value at the end of the URL is generally the value of the method's argument. For example, /Products/Edit/2 matches up with the id argument in ProductsController.Edit(int id). The controller methods normally use this id to query for a single record with a matching id to return to the view.

Below is a table detailing the mappings between routes, controllers, action methods and their parameters.

Action Method Route {controller}/{action}/{id} HTTP POST/GET
public ActionResult Index() { } /Products GET
public ActionResult Details(int id) { } /Products/Details/id GET, values in URL
public ActionResult Edit(int id) { } /Products/Edit/id GET, values in URL
[HttpPost]
public ActionResult Edit(int id, FormCollection collection) { }
/Products/Edit/id POST, values in FormCollection
public ActionResult Create() { } /Products/Create GET
[HttpPost]
public ActionResult Create(FormCollection collection) { }
/Products/Create POST, values in FormCollection
public ActionResult Delete(int id) { } /Products/Delete/id GET, values in URL
[HttpPost]
public ActionResult Delete(int d, FormCollection collection) { }
/Products/Delete/id POST, values in FormCollection

You may have noticed that the Edit, Create, and Delete action methods come overloaded as pairs, and an [HttpPost] attribute decorates one method in each pair. Since [HttpGet] is the default, the other method doesn't need to be marked. Additionally, all methods decorated with the [HttpPost] attribute are meant to accept HTML input fields located between the <form> tags. The collection parameter of type FormCollection[1] contains all the input fields from the <form> of the HTTP Post.

Every method in a controller class returns an ActionResult by default; however, some actions will be more optimized, clear, or accurate in the code, if we are to specify a more specific action results, for instance, a ViewResult, rather than an ActionResult. Action result methods can also designate that its result will return something other than data for a view, e.g., a HttpNotFoundResult or an HttpStatusCodeResult, as shown in the code below:

Returns an HTTP 404 Not found error:

public HttpStatusCodeResult NotFound()
{     
    var result = new HttpStatusCodeResult(404);     
    return result;
}

Redirects to a separate view altogether:

[HttpPost]
public RedirectResult Create(FormCollection collection)
{
    // code to save form data ...     
    return new RedirectResult("Home/Index", false); 
}

There's many more ActionResult types to explore. Check them out and find something that meets the needs of your controller and/or view.

Summary

ActionResults are a key component to ASP.NET MVC, so understanding them is essential for MVC application development. Whether you need to make AJAX calls, redirect, return a file stream, or just return data to a view, ActionResults are right there in the center of all the......action.

[1] I recommend rather than using FormCollection, you should use a strongly typed parameter or multiple, differently typed parameters that match to data types in the HTML form. Look forward to a blog post soon to demonstrate this.

20 Comments

  • Pedro Rosas said

    Hi Rachel,

    Very nice post.
    But... Could you please tell us how you can open the "type tool window" on Visual Studio?
    When I'm hovering over an ActionResult type I only get the standard intellisense info.
    But the tool window you're showing here is quite amazing! How can I get to it?

    Many thanks,
    Pedro

  • James Skemp said

    "You can quickly access the derived types of the ActionResult during development by hovering over an ActionResult in an action result's method signature then expanding the type tool window, to see what *Results the ASP.NET Framework provides."

    What's the trick to this? I'm not seeing it in VS 2010 Pro ... is it Premium/Ultimate only? Or is it just something that needs to get flipped on / installed?

  • Neil Donkin said

    Good post, thanks.

    What is the Visual Studio that shows the Derived Types in your first screenshot?

  • James Skemp said

    (Sorry for the possible double post - I didn't get a message when I first tried.)

    "There are many derived ActionResult types you can use to return results that are more specific for a particular view. You can quickly access the derived types of the ActionResult during development by hovering over an ActionResult in an action result's method signature then expanding the type tool window, to see what *Results the ASP.NET Framework provides."

    I'm not seeing this in VS 2010 Pro. Do I not have this available in my edition, or do I need to set a preference, or install an add-on?

    Thanks!

  • James Alexander said

    The window/tooltip showing derived types and other examples is part of the Productivity Power Tools extension for VS 2010.

  • Vlad said

    You have to install Productivity Power Tools to see that window on first picture. It is here:
    http://go.microsoft.com/fwlink/?LinkId=189202

  • Rachel said

    Yes, as James & Vlad mentioned, you do need the productivity power tools. Thanks for chipping in while I was away, guys.

  • James Skemp said

    Thanks for the answer James and Vlad. Was initially turned off by not being able to turn these features off, so it's glad to see that's changed.

    *Amazing* addition to VS.

    Thanks for the post, Rachel, that brought this to my attention!

  • Derek Morrison said

    Thanks for explaining this so clearly!

    I'm a little dubious about your footnote where you recommend using strongly typed parameters over FormCollection. For example, if you have an edit action for HTTP posts and you're using strongly typed parameters, what do you do if you have a required field (of a non-nullable type), JavaScript is turned off, and you post to that action without the required field? This came up for me when running one of your samples, and I got the error "The parameters dictionary contains a null entry for parameter 'CreationDate' of non-nullable type 'System.DateTime' for method...".

    I've been using FormCollection and handling this scenario by using UpdateModel/TryUpdateModel within the method.

    Maybe this will be detailed in a possible future post that you mentioned.

    Thanks again!

  • water said

    good articles.
    Thanks for your detaied about ActionResult.
    and thanks for the great extension tool "ProPowerTools"

  • Jiten said

    It is a very good stuff.Please give a continution article of using different ActionResult classes and how to use them in Views pages

Comments have been disabled for this content.