top button
Flag Notify
    Connect to us
      Site Registration

Site Registration

Overloading Controller Actions In ASP.NET MVC

+4 votes
517 views

Method overloading is very common technique used in C# code. Although it works great for normal C# classes, the same can't be said about ASP.NET MVC controller classes. If you ever tried to implement method overloading for MVC actions you are probably aware of the following exception:

image

Unlike an ordinary C# class, a controller is dealing with HTTP protocol. And HTTP doesn't understand overloading as C# does. So, in case you wish to use method overloading in a controller you need to consider the four possible ways as discussed in the remainder of this article.

Actions handling different HTTP verbs

In this scenario you have two action methods with the same name BUT they handle different HTTP verbs. Consider the following fragment of code:

public ActionResult Index()
{
    return View();
}

[HttpPost]
public ActionResult Index(Customer obj)
{
    //do something with obj
    return View();
}

As you can see the first Index() action deals with the GET requests whereas the second Index() action deals with POST requests. The first version is parameter-less whereas the second accepts a parameter of type Customer (this depends on what you are submitting from the view. The above example assumes you are submitting customer data).

The above overloaded methods work fine since each is dealing with different HTTP verb.

Actions handling GET requests and attribute routing

You can overload actions dealing with GET requests if you configure attribute routing properly. The following example will make this clear:

[Route("Home/DoWork/{status:int}")]
public ActionResult DoWork(int status)
{
    return View();
}

[Route("Home/DoWork/{flag:bool}")]
public ActionResult DoWork(bool flag)
{
    return View();
}

Now in this case there are two DoWork() actions but one has an integer parameter whereas the other has a boolean parameter. Moreover, [Route] attribute is used to configure attribute based routing. Notice that the first [Route] attribute specifies that status parameter is int and the second specifies that it is bool. So, if a URL is /home/dowork/10 then it will be handled by the first DoWork() whereas if the URL is /home/dowork/true it will be handled by the second DoWork() action.

Make sure you call MapMvcAttributeRoutes() in the RouteConfig.cs file before you test this technique.

Overloaded actions marked as [NonAction]

In this technique you create a public action method as usual. And then create several overloads of it as that are not MVC actions. These overloads are marked as [NonAction] attribute. The following code fragment will make it clear:

[HttpPost]
public ActionResult Calculate()
{
    int i = int.Parse(Request.Form["num1"]);
    int j = int.Parse(Request.Form["num2"]);
    return Calculate(i , j);
}

[NonAction]
public ActionResult Calculate(int i, int j)
{
    return View( i + j );
}

Here, the first method - Calculate() - acts as an MCV action method. The Calculate() then invokes an overload - Calculate(int,int). The overload does its job and returns a view to the caller. Notice that the second Calculate() is marked with [NonAction] attribute. The [NonAction] attribute indicates that the method under consideration should not be treated as an action. Since the method is no longer an action it doesn't interfere in the working of HTTP and the controller.

[ActionName] attribute

 In this technique you create overloaded action methods as you would have done in any C# class. But give them another unique name using the [ActionName] attribute. The following code fragment will make it clear:

[HttpPost]
[ActionName("ProcessForm1")]
public ActionResult ProcessForm(Customer obj)
{
    return View();
}

[HttpPost]
[ActionName("ProcessForm2")]
public ActionResult ProcessForm(Employee emp)
{
    return View();
}

Here, we have two overloads of the ProcessForm() actions. However, both have [ActionName] attribute added on top of them. The [ActionName] attribute assigns a unique name to a method that is then used by the MVC framework. That means for your C# code the method name remains ProcessForm() but for the MVC framework (including routing engine and HTML helpers) they represent two methods - ProcessForm1() and ProcessForm2().

Remember that when you use [ActionName] attribute you are assigning a different name to the action method under consideration. In helpers such as BeginForm() and ActionLink() you should use the name as defined in the [ActionName] attribute (ProcessForm1 and ProcessForm2 in this case).

That's it! Although it is better to keep away from method overloading in the controllers, if at all you wish to implement overloading you can use one of the above ways. As you can see using overloading adds a bit of complexity to your controller.  But it is possible in a limited way if needed.

posted Oct 24, 2016 by Shivaranjini

  Promote This Article
Facebook Share Button Twitter Share Button LinkedIn Share Button


Related Articles

ASP.NET MVC Action Methods are responsible to execute requests and generate responses to it. By default, it generates a response in the form of ActionResult. Actions typically have a one-to-one mapping with user interactions.

For example, enter a URL into the browser, click on any particular link, and submit a form, etc. Each of these user interactions causes a request to be sent to the server. In each case, the URL of the request includes information that the MVC framework uses to invoke an action method. The one restriction on action method is that they have to be instance method, so they cannot be static methods. Also there is no return value restrictions. So you can return the string, integer, etc.

Request Processing

Actions are the ultimate request destination in an MVC application and it uses the controller base class. Let's take a look at the request processing.

  • When a URL arrives, like /Home/index, it is the UrlRoutingModule that inspects and understands that something configured within the routing table knows how to handle that URL.

Request Processing

  • The UrlRoutingModule puts together the information we've configured in the routing table and hands over control to the MVC route handler.

  • The MVC route handler passes the controller over to the MvcHandler which is an HTTP handler.

  • MvcHandler uses a controller factory to instantiate the controller and it knows what controller to instantiate because it looks in the RouteData for that controller value.

  • Once the MvcHandler has a controller, the only thing that MvcHandler knows about is IController Interface, so it simply tells the controller to execute.

  • When it tells the controller to execute, that's been derived from the MVC's controller base class. The Execute method creates an action invoker and tells that action invoker to go and find a method to invoke, find an action to invoke.

  • The action invoker, again, looks in the RouteData and finds that action parameter that's been passed along from the routing engine.

Types of Action

Actions basically return different types of action results. The ActionResult class is the base for all action results. Following is the list of different kind of action results and its behavior.

Sr.No.Name and Behavior
1

ContentResult

Returns a string

2

FileContentResult

Returns file content

3

FilePathResult

Returns file content

4

FileStreamResult

Returns file content

5

EmptyResult

Returns nothing

6

JavaScriptResult

Returns script for execution

7

JsonResult

Returns JSON formatted data

8

RedirectToResult

Redirects to the specified URL

9

HttpUnauthorizedResult

Returns 403 HTTP Status code

10

RedirectToRouteResult

Redirects to different action/different controller action

11

ViewResult

Received as a response for view engine

12

PartialViewResult

Received as a response for view engine

Let’s have a look at a simple example from the previous chapter in which we have created an EmployeeController.

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCControllerDemo.Controllers {
   public class EmployeeController : Controller{
      // GET: Employee
      public ActionResult Search(string name){
         var input = Server.HtmlEncode(name);
         return Content(input);
      }
   }
}

When you request the following URL http://localhost:61465/Employee/Mark, then you will receive the following output as an action.

Localhost Employee Mark Output

Add Controller

Let us add one another controller.

Step 1 − Right-click on Controllers folder and select Add → Controller.

Add Another Controller

It will display the Add Scaffold dialog.

Add Scaffolding Dialog

Step 2 − Select the MVC 5 Controller – Empty option and click ‘Add’ button.

The Add Controller dialog will appear.

CustomerController

Step 3 − Set the name to CustomerController and click ‘Add’ button.

Now you will see a new C# file ‘CustomerController.cs’ in the Controllers folder, which is open for editing in Visual Studio as well.

set_name CustomerController

Similarly, add one more controller with name HomeController. Following is the HomeController.cs class implementation.

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCControllerDemo.Controllers {
   public class HomeController : Controller{
      // GET: Home
      public string Index(){
         return "This is Home Controller";
      }
   }
}

Step 4 − Run this application and you will receive the following output.

Home Controller Output

Step 5 − Add the following code in Customer controller, which we have created above.

public string GetAllCustomers(){
   return @"<ul>
      <li>Ali Raza</li>
      <li>Mark Upston</li>
      <li>Allan Bommer</li>
      <li>Greg Jerry</li>
   </ul>";
}

Step 6 − Run this application and request for http://localhost:61465/Customer/GetAllCustomers. You will see the following output.

Localhost GetAllCustomers

You can also redirect to actions for the same controller or even for a different controller.

Following is a simple example in which we will redirect from HomeController to Customer Controller by changing the code in HomeController using the following code.

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCControllerDemo.Controllers{
   public class HomeController : Controller{
      // GET: Home
      public ActionResult Index(){
         return RedirectToAction("GetAllCustomers","Customer");
      }
   }
}

As you can see, we have used the RedirectToAction() method ActionResult, which takes two parameters, action name and controller name.

When you run this application, you will see the default route will redirect it to /Customer/GetAllCustomers

Localhost Customers GetAllCustomers

 

READ MORE

At times you need to pass data from an action method belonging to one controller to an action method belonging to another controller. There are three ways to accomplish this task. They are:

  • Pass data as query string parameters
  • Pass data in TempData dictionary
  • Pass data as route parameters

 Let's quickly see how each of these three approaches work.

Pass data as query string parameters

This approach is possibly the most primitive one and involves no special consideration from your side. The action method sending the data can use Redirect() method or RedirectToAction() method to transfer the control to the receiving action method. The following code shows how this is done:

public ActionResult Index()
{
    Customer data = new Customer()
    {
        CustomerID = 1,
        CustomerName = "Abcd",
        Country = "USA"
    };
    string url=string.Format("/home2/index?customerid={0}
               &customername={1}&country={2}",
               data.CustomerID,data.CustomerName,data.Country);
    return Redirect(url);
}

The above code shows Index() action from Home1 controller. The Index action method instantiates Customer object - the model class - that looks like this:

public class Customer
{
    public int CustomerID { get; set; }
    public string CustomerName { get; set; }
    public string Country { get; set; }
}

It then forms a URL pointing to the Index() action from Home2 controller. Notice how data from Customer object is transferred in the form of query string parameters. In the above example there is no logic for generating model data but in a more realistic case you may have such a logic in this method. And once the data is generated you can pass it to the another controller using query string. The Redirect() method then takes the control to the Index() action of Home2 controller.

The Index() action of Home2 can receive the data as shown below:

public ActionResult Index()
{
    Customer data = new Customer();
    data.CustomerID = int.Parse(Request.QueryString["CustomerID"]);
    data.CustomerName = Request.QueryString["CustomerName"];
    data.Country = Request.QueryString["Country"];
    return View(data);
}

As you can see Request.QueryString collection is being used to read the values passed in the query string. Once a Customer object is formed, it is passed to the Index view.

This technique has an advantage that it is quite simple and requires no additional configuration. However, it's bit crude technique and you should avoid it if any of the other techniques can be used.

Pass data using TempData dictionary

In this technique you store the data to be passed in the TempData dictionary in the sender action method. The receiving action method reads the data from the TempData dictionary. You might be aware that TempData dictionary can hold data till it is read and this can carry data across multiple requests. The following code shows how this is done:

public ActionResult Index()
{
    Customer data = new Customer()
    {
        CustomerID = 1,
        CustomerName = "Abcd",
        Country = "USA"
    };
    TempData["mydata"] = data;
    return RedirectToAction("Index", "Home2");
}

As you can see Index() action instantiates a Customer object as before. This time, however, it stores the Customer object in a TempData key named mydata. The RedirectToAction() method is then used to take the control to the Index() action of Home2 controller.

Inside the Index() of Home2, you can read the value as follows:

public ActionResult Index()
{
    Customer data = TempData["mydata"] as Customer;
    return View(data);
}

The above code reads the Customer object from TempData dictionary and passes it to the Index view.

The TempData technique doesn't require any additional setup but it requires that session state be enabled. Also, TempData is designed to store arbitrary pieces of data. If you are planning to send model objects through TempData, that may be a deviation from standard design practices.

Pass data as route parameters

In this technique you need to do an additional work of defining a route in the system. For example, if you wish to pass the Customer data in the form of route parameters you need to define a route like this:

routes.MapRoute(
    name: "Default2",
    url: "{controller}/{action}/
          {customerid}/{customername}/{country}",
    defaults: new { controller = "Home2", action = "Index" }
);

As shown above, the route includes {customerid}, {customername}, and {country} route parameters and the route is mapped with Index() action of Home2 controller. Once the above configuration is done you can pass data from the sender action as follows:

public ActionResult Index1()
{
    Customer data = new Customer()
    {
        CustomerID = 1,
        CustomerName = "Abcd",
        Country = "USA"
    };
    return RedirectToAction("Index", "Home2", data);
}

Notice that, this time the Customer object is passed as the third parameter of RedirectToAction() method. This way Customer data will be passed in the form of route parameter values. To receive this data the receiver action method should write something like this:

public ActionResult Index()
{
    Customer data = new Customer();
    UpdateModel(data);
    return View(data);
}

// OR

public ActionResult Index(Customer data)
{
    return View(data);
}

As shown above you can either use UpdateModel() method to transfer values from the route to the Customer object or you can have a parameter to the Index() action method.

This technique is quite clean and makes use of MVC specific parts (route and route parameters). It doesn't have any dependency on session state as in the case of TempData technique. On the other hand you need to create a route to deal with the route parameters.

Note about query string and route parameters

Before I conclude this post, it would be interesting to see a small thing about how RedirectToAction() deals with query string and route parameters.

Let's assume that your sender action is like this:

public ActionResult Index()
{
    Customer data = new Customer()
    {
        CustomerID = 1,
        CustomerName = "Abcd",
        Country = "USA"
    };
    return RedirectToAction("Index", "Home2", data);
}

Notice that RedirectToAction() method passes Customer data object to Index() of Home2 as the third parameter.

The interesting thing to note is - If you haven't defined any route to match the data MVC sends it as query string. And if you have defined a route, it passes the values as route parameters. You can confirm this by observing the browser address bar once the Index() of Home2 renders the view. The following figure shows the difference:

image

That also means in the query string technique discussed earlier, you could have used exactly same code in the receiving action as in the case of route parameter technique.

In addition to the three techniques discussed above you can also use Session object but that's not discussed here separately since TempData technique anyway depends on the session storage.

That's it for now. Keep coding!

READ MORE
...