0

I have the following in my controller:

    public ActionResult Details(string name)
    {
        Student student = db.Students.FirstOrDefault(x => x.FirstName == name);
        if (student == null)
        {
            return HttpNotFound();
        }
        return View(student);
    }

    // GET: /Student/Details/5
    public ActionResult Details(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Student student = db.Students.Find(id);
        if (student == null)
        {
            return HttpNotFound();
        }
        return View(student);
    }

RouteConfig.cs:

        // /Student/Details/Id
        routes.MapRoute(
            name: "StudentDetailsId",
            url: "{Student}/{Action}/{id}",
            defaults: new { controller = "Student", action = "Details", id = UrlParameter.Optional },
            constraints: new { id = @"\d+" }
        );

        // /Student/Details/Name
        routes.MapRoute(
            name: "StudentDetailsName",
            url: "{Student}/{Details}/{name}",
            defaults: new { controller = "Student", action = "Details" }
        );

        // /Student/Name
        routes.MapRoute(
            name: "StudentName",
            url: "{Student}/{name}",
            defaults: new { controller = "Student", action = "Details" }
        );

So pretty much I would like to have the same action name but fetched with either an id:int or string.

However I get the following:

The current request for action 'Details' on controller type 'StudentController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Details(System.String) on type MySuperAwesomeMVCApp.Controllers.StudentController
System.Web.Mvc.ActionResult Details(System.Nullable`1[System.Int32]) on type MySuperAwesomeMVCApp.Controllers.StudentController
tereško
  • 58,060
  • 25
  • 98
  • 150
ShaneKm
  • 20,823
  • 43
  • 167
  • 296
  • Possible duplicate of http://stackoverflow.com/q/10668105/304832 – danludwig Nov 05 '13 at 14:03
  • Next time, do a search for your exception message. The answer is the first result: https://www.google.com/search?q=the+current+request+for+action+is+ambiguous+between+the+following+action+methods – danludwig Nov 05 '13 at 14:05

1 Answers1

2

Controller:

public ActionResult DetailsByName(string name)
{
    Student student = db.Students.FirstOrDefault(x => x.FirstName == name);
    if (student == null)
    {
        return HttpNotFound();
    }
    return View(student);
}

// GET: /Student/Details/5
public ActionResult DetailsById(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Student student = db.Students.Find(id);
    if (student == null)
    {
        return HttpNotFound();
    }
    return View(student);
}

Routes:

 // /Student/Details/Id
    routes.MapRoute(
        name: "StudentDetailsId",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Student", action = "DetailsById", id = UrlParameter.Optional },
        constraints: new { id = @"\d+" }
    );

    // /Student/Details/Name
    routes.MapRoute(
        name: "StudentDetailsName",
        url: "{controller}/{action}/{name}",
        defaults: new { controller = "Student", action = "DetailsByName" }
    );

    // /Student/Name
    routes.MapRoute(
        name: "StudentName",
        url: "{controller}/{name}",
        defaults: new { controller = "Student", action = "DetailsByName" }
    );

In MVC, you can only have a maximum of 2 methods with the same name, and when you do, one of them must be GET and the other must be POST.

No, MVC cannot figure out which action method you want to invoke given a set of route data. Though your methods are valid C# overloads, MVC's action method selector is not strongly-typed. I have answered this question before, let me look up the link...

Update

Here is the link: https://stackoverflow.com/a/10668537/304832

Another Update

I only bring this up because other people have thanked me for mentioning it when I answer other routing questions.... Look into AttributeRouting.NET. It is so great that it's the most talked about feature of MVC5, though it started out as an open source side project and there is no reason why you can't use it in MVC4. Just download the NuGet package. With this library, you don't need to use MapRoute. Your action methods would look like this:

// GET: /Students/Details/5
[GET("students/details/{id:int}", ControllerPrecedence = 1)]
public ActionResult DetailsById(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Student student = db.Students.Find(id);
    if (student == null)
    {
        return HttpNotFound();
    }
    return View(student);
}

// GET: /Students/Details/Albert+Einstein
// or GET: /Students/Albert+Einstein
[GET("students/{name}", ActionPrecedence = 1)]
[GET("students/details/{name}")]
public ActionResult DetailsByName(string name)
{
    Student student = db.Students.FirstOrDefault(x => x.FirstName == name);
    if (student == null)
    {
        return HttpNotFound();
    }
    return View(student);
}

No MapRoute or cryptic regular expression constraints necessary. You just have to make sure your int method has precedence so that MVC doesn't try to pass numbers into your method that takes a string.

Community
  • 1
  • 1
danludwig
  • 46,965
  • 25
  • 159
  • 237
  • so I can't have identically named actions? I thought route config would be able to figure out the difference by int vs. string – ShaneKm Nov 05 '13 at 13:57
  • You can have identically named actions, but either they have to be on different controllers, or if they are on the same controller, one must be a GET and the other must be a POST. – danludwig Nov 05 '13 at 14:00
  • P.S. -- you can get away with identically named action methods by using the `[ActionName(string)]` attribute on them. In your route definitions, refer to the attribute's name rather than the compiled method's name. There is more on that in the other answer I linked to. – danludwig Nov 05 '13 at 14:11