Можете ли вы перегружать методы контроллера в ASP.NET MVC? - PullRequest
316 голосов
/ 12 января 2009

Мне интересно посмотреть, можно ли перегрузить методы контроллера в ASP.NET MVC. Всякий раз, когда я пытаюсь, я получаю ошибку ниже. Два метода принимают разные аргументы. Это что-то, что не может быть сделано?

Текущий запрос на действие «MyMethod» для типа контроллера «MyController» неоднозначен между следующими методами действия:

Ответы [ 16 ]

199 голосов
/ 12 января 2009

Вы можете использовать атрибут, если хотите, чтобы ваш код перегружался.

[ActionName("MyOverloadedName")]

Но вам придется использовать другое имя действия для того же метода http (как уже говорили другие). Так что это просто семантика на тот момент. Вы бы предпочли, чтобы имя было в вашем коде или атрибуте?

У Фила есть статья, связанная с этим: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

69 голосов
/ 12 января 2009

Да. Я смог сделать это, установив HttpGet / HttpPost (или эквивалентный атрибут AcceptVerbs) для каждого метода контроллера на что-то отдельное, то есть HttpGet или HttpPost, но не оба. Таким образом, в зависимости от типа запроса он может определить, какой метод использовать.

[HttpGet]
public ActionResult Show()
{
   ...
}

[HttpPost]
public ActionResult Show( string userName )
{
   ...
}

Одно из предложений, которое у меня есть, заключается в том, что для подобного случая будет иметь частную реализацию, на которую полагаются оба ваших открытых метода Action, чтобы избежать дублирования кода.

41 голосов
/ 29 марта 2011

Вот еще кое-что, что вы могли бы сделать ... вам нужен метод, который может иметь параметр, а не.

Почему бы не попробовать это ...

public ActionResult Show( string username = null )
{
   ...
}

Это сработало для меня ... и в этом одном методе вы можете проверить, есть ли у вас входящий параметр.


Обновлено, чтобы удалить недопустимый синтаксис Nullable для строки и использовать значение параметра по умолчанию.
20 голосов
/ 28 ноября 2014

Нет, Нет и Нет. Идите и попробуйте код контроллера ниже, где у нас перегружен «LoadCustomer».

public class CustomerController : Controller
    {
        //
        // GET: /Customer/

        public ActionResult LoadCustomer()
        {
            return Content("LoadCustomer");
        }
        public ActionResult LoadCustomer(string str)
        {
            return Content("LoadCustomer with a string");
        }
    }

Если вы попытаетесь вызвать действие «LoadCustomer», вы получите ошибку, как показано на рисунке ниже.

enter image description here

Полиморфизм является частью программирования на C #, а HTTP является протоколом. HTTP не понимает полиморфизм. HTTP работает над концепцией или URL, а URL может иметь только уникальные имена. Так что HTTP не реализует полиморфизм.

Чтобы исправить это, нам нужно использовать атрибут «ActionName».

public class CustomerController : Controller
    {
        //
        // GET: /Customer/

        public ActionResult LoadCustomer()
        {
            return Content("LoadCustomer");
        }

        [ActionName("LoadCustomerbyName")]
        public ActionResult LoadCustomer(string str)
        {
            return Content("LoadCustomer with a string");
        }
    }

Так что теперь, если вы сделаете вызов URL «Customer / LoadCustomer», будет вызвано действие «LoadCustomer», а со структурой URL «Customer / LoadCustomerByName» будет вызвано «LoadCustomer (string str)».

enter image description here

enter image description here

Ответ выше, который я взял из этой статьи codeproject -> Перегрузка действия MVC

15 голосов
/ 17 февраля 2010

Чтобы преодолеть эту проблему, вы можете написать ActionMethodSelectorAttribute, который проверяет MethodInfo для каждого действия и сравнивает его с опубликованными значениями формы, а затем отклоняет любой метод, для которого значения формы не совпадение (исключая название кнопки, конечно).

Вот пример: - http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

НО, это не очень хорошая идея.

13 голосов
/ 12 января 2009

Насколько я знаю, вы можете использовать один и тот же метод только при использовании различных методов http.

т.е.

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

}

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

}
9 голосов
/ 03 июня 2015

Я добился этого с помощью Маршрутизация атрибутов в MVC5. По общему признанию я новичок в MVC, пришедшем из десятилетия веб-разработки с использованием WebForms, но мне помогло следующее. В отличие от принятого ответа, это позволяет отображать все перегруженные действия одним и тем же файлом представления.

Сначала включите маршрутизацию атрибутов в App_Start / RouteConfig.cs.

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapMvcAttributeRoutes();

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );            
    }
}

При желании можно украсить класс контроллера префиксом маршрута по умолчанию.

[RoutePrefix("Returns")]
public class ReturnsController : BaseController
{
    //.......

Затем украсьте действия вашего контроллера, которые перегружают друг друга, общим маршрутом и параметрами, которые вам подходят. Используя ограниченные по типу параметры, вы можете использовать один и тот же формат URI с идентификаторами разных типов.

[HttpGet]
// Returns
public ActionResult Index()
{
    //.....
}

[HttpGet]
[Route("View")]
// Returns/View
public ActionResult View()
{
    // I wouldn't really do this but it proves the concept.
    int id = 7026;
    return View(id);
}

[HttpGet]
[Route("View/{id:int}")]
// Returns/View/7003
public ActionResult View(int id)
{
    //.....
}

[HttpGet]
[Route("View/{id:Guid}")]
// Returns/View/99300046-0ba4-47db-81bf-ba6e3ac3cf01
public ActionResult View(Guid id)
{
    //.....
}

Надеюсь, что это помогает и не ведет кого-то по ложному пути. : -)

4 голосов
/ 04 февраля 2016

Я только что натолкнулся на этот вопрос, и хотя он довольно старый, он все еще очень актуален. По иронии судьбы, один правильный комментарий в этой теме был опубликован новичком в MVC, который сам признался, когда писал эту статью. Даже документы ASP.NET не совсем корректны. У меня большой проект, и я успешно перегружаю методы действий.

Если кто-то понимает маршрутизацию, помимо простого шаблона маршрута по умолчанию {controller} / {action} / {id}, может быть очевидно, что действия контроллера могут быть отображены с использованием любого уникального шаблона. Кто-то здесь говорил о полиморфизме и сказал: «HTTP не понимает полиморфизм», но маршрутизация не имеет ничего общего с HTTP. Проще говоря, это механизм для сопоставления строковых шаблонов.

Лучший способ выполнить эту работу - использовать атрибуты маршрутизации, например:

[RoutePrefix("cars/{country:length(3)}")]
public class CarHireController
{
    [Route("{location}/{page:int=1}", Name = "CarHireLocation")]
    public ActionResult Index(string country, string location, int page)
    {
        return Index(country, location, null, page);
    }

    [Route("{location}/{subLocation}/{page:int=1}", Name = "CarHireSubLocation")]
    public ActionResult Index(string country, string location, string subLocation, int page)
    {
        //The main work goes here
    }
}

Эти действия будут относиться к URL-адресам, таким как /cars/usa/new-york и /cars/usa/texas/dallas, которые будут отображаться в первом и втором индексных действиях соответственно.

Изучив этот пример контроллера, очевидно, что он выходит за рамки шаблона маршрута по умолчанию, упомянутого выше. Значение по умолчанию работает хорошо, если ваша структура URL точно соответствует соглашениям об именах кода, но это не всегда так. Код должен описывать домен, но URL-адреса часто должны идти дальше, потому что их содержание должно основываться на других критериях, таких как требования SEO.

Преимущество шаблона маршрутизации по умолчанию заключается в том, что он автоматически создает уникальные маршруты. Это обеспечивается компилятором, поскольку URL-адреса будут соответствовать уникальным типам и членам контроллера. Прокрутка собственных шаблонов маршрутов потребует тщательного обдумывания, чтобы обеспечить уникальность и то, что они работают.

Важное примечание Единственный недостаток заключается в том, что использование маршрутизации для генерации URL-адресов для перегруженных действий не работает, если основано на имени действия, например, при использовании UrlHelper.Action. Но это работает, если использовать именованные маршруты, например, UrlHelper.RouteUrl. И использование названных маршрутов, согласно хорошо уважаемым источникам, является путем, чтобы пойти так или иначе (http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/).

Удачи!

4 голосов
/ 03 декабря 2012

Вы можете использовать один ActionResult для работы с Post и Get:

public ActionResult Example() {
   if (Request.HttpMethod.ToUpperInvariant() == "GET") {
    // GET
   }
   else if (Request.HttpMethod.ToUpperInvariant() == "POST") {
     // Post  
   }
}

Полезно, если ваши методы Get и Post имеют совпадающие подписи.

3 голосов
/ 15 июля 2017

Вы можете использовать [ActionName ("NewActionName")], чтобы использовать тот же метод с другим именем:

public class HomeController : Controller
{
    public ActionResult GetEmpName()
    {
        return Content("This is the test Message");
    }

    [ActionName("GetEmpWithCode")]
    public ActionResult GetEmpName(string EmpCode)
    {
        return Content("This is the test Messagewith Overloaded");
    }
}
...