ASP.NET MVC: ActionNameSelector and ActionMethodSelector (or another approach to submit form to multiple action methods) – Part I

Note: Thank you for visiting my blog. However, this blog is not maintained actively anymore. This post is now also in my new blog. Feel free to leave a comment there.

At work I have recently faced a requirement to post a single form to multiple Action methods in a controller based on the context. For example, in the view there is a Form and there are several radio buttons; now my form should be posted to the Action method based on the radio button selection.
As usual I Googled it and found that internet is glutted with various approaches. Some of them are: 1. Posting to single action method and use a switch mechanism inside it, 2. Using multiple forms, 3. Using named submit button and assigning different values to those.
However there is one concept that I liked most and that is making use of ActionNameSelector and ActionMethodSelector.

What are these?
Well these are attributes that influence/affects the selection of action methods… yes you got it right. I’ve quoted it from MSDN and just like some of you (maybe) I didn't understood a thing when I read those lines... ;)
You can check the below MSDN links:
ActionNameSelectorAttribute Class, ActionMethodSelectorAttribute Class.
Hmmm… so what are these anyway. 


Just as the names suggest,  
ActionNameSelector selects the Action methods matching name with the one in RouteData, from the Action methods available in the controller 
and 
ActionMethodSelector selects one single method from the ones selected by ActionNameSelector. 
 

Still not clear?

Well, here are some examples which may help.
One ActionNameSelector attribute that is shipped with ASP.NET MVC is ActionNameAttribute that you use to give same name to two or more action methods in your controller. 
 
And HttpPost  is an example of ActionMethodSelector attribute that is shipped with ASP.NET MVC that you use to select the action method at the time of Form Post. 
 
You must check the GitHub links: ActionMethodSelector, HttpPost, AcceptVerbsAttribute



So imagine you have a Home Controller and two Action methods both named Index, one for GET and another for POST. Now when the form is posted, MVC’s default action name selector attribute selects both the action methods as RouteData has the action name as “Index” (considering you are posting the form to the Index Action method only). Now after ActionNameSelector is done with its job, ActionMethodSelector comes into picture and tries to find out the ONE action method that may do the job. Now consider hypothetically that there is no ActionMethodSelector (or any other influencing factor) present, hence at this point you have both the action methods selected. And if that is the case having two Action Methods selected that can’t be filtered further, MVC will raise an exception of ambiguous action methods. As you may already know that one and only action method needs to be selected at end, otherwise MVC will raise exception (Note that parameters in action methods has nothing to with influencing the selection of action method).

So, ActionMethodSelector does the second level of filtering.

The same thing is explained in the below code sample:
Assume you have following controller:
   1:      public class HomeController : Controller
   2:      {
   3:          //
   4:          // GET: /Home/
   5:   
   6:          public ActionResult Index()
   7:          {
   8:              ViewBag.Msg = "GET Index";
   9:              return View();
  10:          }
  11:          
  12:          public ActionResult Index(int id)
  13:          {
  14:              return View();
  15:          }
  16:   
  17:      }


When you run this application, you get following exception as both Index methods are selected by MVC’s default Action Method Selector.




So to fix this (and also to POST your form properly) you put a [HttpPost] attribute on the top of the second action method which looks like below and your controller executes fine now:


   1:   public class HomeController : Controller
   2:      {
   3:          //
   4:          // GET: /Home/
   5:   
   6:          public ActionResult Index()
   7:          {
   8:              ViewBag.Msg = "GET Index";
   9:              return View();
  10:          }
  11:          
  12:          [HttpPost]
  13:          public ActionResult Index(int id)
  14:          {
  15:              return View();
  16:          }
  17:   
  18:      }



As already told HttpPost is an Action Method Selector and filters(out in this case) the proper action method to be executed.


Lets add a bit more complexity by adding a new Action Method and add an ActionNameAttribute - which portrays the new action method  as another “Index” - and an HttpPost on top of that, just like below and try to post the form again:

Controller:



   1:      public class HomeController : Controller
   2:      {
   3:          public ActionResult Index()
   4:          {
   5:              return View();
   6:          }
   7:          
   8:          [HttpPost]
   9:          public ActionResult Index(int id)
  10:          {
  11:              return View();
  12:          }
  13:   
  14:          [HttpPost,ActionName("Index")]
  15:          public ActionResult JustAnotherAction(int id)
  16:          {
  17:              return View();
  18:          }
  19:   
  20:      }



View:



   1:  @{
   2:      ViewBag.Title = "Index";
   3:  }
   4:   
   5:  <h2>Index</h2>
   6:   
   7:  @using(Html.BeginForm()){
   8:  @:ID: <input name="id" type="text" />
   9:      <input type="submit" value="Submit"/>
  10:  }



So now if you try to post the Form you will get the following error:



Now if you change the HttpPost attribute on JustAnotherAction - the third action method – to HttpDelete or HttpPut, it will not be selected when you again post the form and hence the MVC application will not raise any error.


That’s all for today. I wish to write another part of this story depicting how to make your own ActionMethodSelector, the link for which I’ll provide very shortly (at least that is what I believe).

Hope this helps you.


Thanks,
Sayan.

P.S.: Here is the link to the second part of the article.

0 comments: (+add yours?)

Post a Comment