views:

5522

answers:

6

Im curious to see if you can overload controller methods in ASP.Net MVC. Whenever I try, I get the error below. The two methods accept different arguements. Is this something that cannot be done?

The current request for action 'MyMethod' on controller type 'MyController' is ambiguous between the following action methods:

+2  A: 

As far as I know you can only have the same method when using different http methods.

i.e.

[AcceptVerbs("GET")]
public ActionResult MyAction()
{

}

[AcceptVerbs("POST")]
public ActionResult MyAction(FormResult fm)
{

}
keeney
+10  A: 

Yes. I've been able to do this by setting the AcceptVerbs attribute for each controller method to something distinct, i.e., HttpVerbs.Get or HttpVerbs.Post, but not both. That way it can tell based on the type of request which method to use.

[AcceptVerbs( HttpVerbs.Get )]
public ActionResult Show()
{
   ...
}

[AcceptVerbs( HttpVerbs.Post )]
public ActionResult Show( string userName )
{
   ...
}

One suggestion I have is that, for a case like this, would be to have a private implementation that both of your public Action methods rely on to avoid duplicating code.

tvanfosson
+23  A: 

You can use the attribute if you want your code to do overloading.

[ActionName("MyOverloadedName")]

But, you'll have to use a different action name for the same http method (as others have said). So it's just semantics at that point. Would you rather have the name in your code or your attribute?

Phil has an article related to this: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

JD Conley
The main downfall of using this and overloading your action is that it can no longer be rendered by the same view file.
Jeff Martin
+3  A: 

To overcome this problem you can write an ActionMethodSelectorAttribute that examines the MethodInfo for each action and compares it to the posted Form values and then rejects any method for which the form values don't match (excluding the button name, of course).

Here's an example:- http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

Hightechrider
A: 

It is tempting to try overloading actions when one has partial views with multiple forms. But it is the wrong strategy. The trick to embedding partial views with multiple forms is to

  1. Name the action according to the view, as you normally would, whether full or partial
  2. Each form specifies its action by utilizing:

    using (Html.BeginForm("Action", "Controller"))

  3. All actions return the parent view

In the following example, there are three views, one full and two partial. The first view contains the second two:

  • ManageUser.aspx
  • ManageUserPassword.ascx
  • ManageUserRoles.ascx

The three associated actions are shown below. Note that each action is named after its corresponding view; however, they each return to the parent:

return View("ManageUser", user);

Good luck!

--Brett

// **************************************
// URL: /Admin/ManageUser/id 
// **************************************

[Authorize]
public ActionResult ManageUser(string id)
{
    MembershipUser user = MembershipService.GetUser(id, false);
    ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
    return View("ManageUser", user);
}

[Authorize]
[HttpPost]
public ActionResult ManageUserPassword(AdminChangePasswordModel model)
{
    if (ModelState.IsValid)
    {
        if (ChangePasswordLogic())
        {
            TempData["Confirmation"] = "Password successfully updated.";
        }
        else
        {
            ModelState.AddModelError("", "Change password failed.");
        }
    }

    // Return the parent view
    MembershipUser user = MembershipService.GetUser(model.UserName, false); 
    ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
    return View("ManageUser", user);
}

    [Authorize]
    [HttpPost]
    public ActionResult ManageUserRoles(AdminRolesModel model)
    {
        if (ModelState.IsValid)
        {
            try
            {
                ChangeRolesLogic();
                TempData["Confirmation"] = "Roles successfully updated.";
            }
            catch (Exception)
            {
                ModelState.AddModelError("", "Role update failed.");
            }
        }

        // Return the parent view
        MembershipUser user = MembershipService.GetUser(model.UserName, false);
        ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
        return View("ManageUser", user);
    }
Brett
A: 

Brett, now your client has the same page at 4 different URLs in their cache and history. Further, if I navigate backward, Firefox will restore the data I entered, which probably doesn't correspond to the name of the page on that trip.

To do that, you should have an intermediary like a PRG

Shannon