Можете ли вы перегрузить методы контроллера в ASP.NET MVC?

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

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

@andy то же самое и для mvc 4 :)

basarat 10.04.2013 10:34

И то же самое для mvc 5

DhruvJoshi 23.08.2015 11:44

И то же самое для mvc 6

Imad 01.04.2016 17:31

То же самое для MVC Core 1.1

kall2sollies 20.01.2017 02:05

То же самое для MVC Core 2.0

Guilherme 01.09.2017 22:52

Кто знает, может быть, для MVC Core 3.0 все по-другому [это не]

Kapé 24.05.2019 18:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
334
6
167 024
17
Перейти к ответу Данный вопрос помечен как решенный

Ответы 17

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

т.е.

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

}

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

}

украшения не имеют никакого отношения к перегрузке. это список параметров, допускающих перегрузку.

Sky Sanders 14.02.2013 13:01

@SkySanders Я не согласен, перегрузка на основе параметров не работает в методах контроллера MVC - у вас есть рабочий пример? Ваше здоровье.

Chalky 15.06.2015 07:51

Используйте атрибут [HttpPost] вместо [AcceptVerbs("POST")].

Fred 25.04.2016 16:22

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

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

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

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

С MVC2 и выше можно также использовать атрибут HttpPost / HttpGet.

yoel halb 13.12.2012 21:22

@yohal Да, это был бы канонический способ справиться с этим сейчас, если вам не нужно поддерживать несколько глаголов.

tvanfosson 14.12.2012 00:10

Только будьте осторожны, не злоупотребляйте этим, чтобы не нарушать принципы REST.

Fred 25.04.2016 16:21

Совершенно уверен, что это работает только потому, что ваши методы Show() имеют разные сигнатуры. Если и когда вам нужно отправить информацию в версию Get, ваши версии Get и Post будут иметь одинаковую подпись, и вам понадобится атрибут ActionName или одно из других исправлений, упомянутых в этом сообщении.

Scott Fraley 16.12.2016 02:35

@ ScottK.Fraley, это правда. Если им нужна одна и та же подпись, вам придется назвать их по-другому и применить ActionNameAttribute. На практике я редко обнаруживал, что это так.

tvanfosson 16.12.2016 02:40

Я случайно нашел эту статью / сообщение, потому что это проблема, с которой я столкнулся прямо сейчас, и фактически в нескольких местах моего текущего проекта. Я согласен, что это редкость. :)

Scott Fraley 16.12.2016 02:58

При переименовании имен действий у меня случайно было одно из действий, украшенное атрибутом [Route ("xxxx")], имя которого совпадало с именем действия. Остерегайтесь этого, так как это испортит вашу попытку перегрузки.

brsfan 10.04.2018 19:15
Ответ принят как подходящий

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

[ActionName("MyOverloadedName")]

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

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

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

Jeff Martin 03.04.2010 21:15

Фактически, он все еще может отображать тот же файл представления. Вам просто нужно указать имя представления вместо того, чтобы вслепую вызывать return View();. Например: return View("MyOverloadedName");.

EAMann 16.09.2011 00:45

@JD, но Microsoft говорит .. Метод, используемый в качестве действия контроллера, не может быть перегружен .. Вы можете увидеть это здесь ..asp.net/mvc/tutorials/controllers-and-routing/…

himanshupareek66 07.06.2014 07:19

@EAMann Приятно, до сих пор я всегда определял весь путь для представления

Alexander Derck 23.03.2017 11:18

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

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

НО, это плохая идея.

@Cerbrus, потому что это ужасный взлом, и следующий человек, который посмотрит на код вашего контроллера, будет сбит с толку очень нестандартным подходом.

Ian Mercer 11.03.2020 19:24

Хех, достаточно честно.

Cerbrus 11.03.2020 19:55

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

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

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

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


Updated to remove the invalid nullable syntax on string and use a default parameter value.

(string не может иметь значение NULL.)

Josh M. 14.06.2011 01:02

строка может допускать значение NULL. Фактически, он уже имеет значение NULL, просто не нужно '?'

ProfK 14.08.2011 15:18

@ProfK - Нет, строка - это ссылочный тип, который может быть нулевым. Это не "обнуляемое". Nullable означает, что вы используете Nullable <T> (т.е. T?). Суть Джоша в том, что вы не можете поставить? после строки, потому что это не тип значения, а Nullable <T> принимает только типы значений.

Erik Funkenbusch 28.08.2011 11:06

Я случайно нашел способ вернуться к этому вопросу, а затем понял, что опубликовал комментарий выше. Не припомню этого ... странного! По-прежнему верно, что string не может быть nullable; но это может быть null! Так или иначе, я опубликовал первоначальный комментарий без искренности.

Josh M. 24.09.2011 00:43

Создайте базовый метод как виртуальный

public virtual ActionResult Index()

Создайте переопределенный метод как переопределение

public override ActionResult Index()

Обновлено: это, очевидно, применяется только в том случае, если метод переопределения находится в производном классе, который, похоже, не был намерением OP.

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

Ace 31.05.2012 17:35

@Andiih: что произойдет, если оба метода находятся в одном контроллере?

Dharmik Bhandari 02.10.2012 17:19

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

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

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

Хм, вроде - снова изобретаю колесо, но на этот раз квадратной формы. Почему бы просто не использовать атрибуты [HttpPost / Get]?

SOReader 27.02.2013 13:10

это было давно, но я думаю, что сделал это, потому что MVC не различал два отдельных метода с соответствующими знаками. Я использовал атрибут HttpPost, но не использовал HttpGet для другого метода.

DevDave 27.02.2013 23:29

@DevDave, а также приписывая оба метода, убедитесь, что вы используете атрибуты из system.web.mvc, а не из system.web.http!

Chalky 15.06.2015 07:49

Мне нужна была перегрузка для:

public ActionResult Index(string i);
public ActionResult Index(int groupId, int itemId);

Было мало аргументов, из-за которых я закончил так:

public ActionResult Index(string i, int? groupId, int? itemId)
{
    if (!string.IsNullOrWhitespace(i))
    {
        // parse i for the id
    }
    else if (groupId.HasValue && itemId.HasValue)
    {
        // use groupId and itemId for the id
    }
}

Это не идеальное решение, особенно если у вас много аргументов, но мне оно подходит.

Мне нравится этот ответ, опубликованный в другой теме

Это в основном используется, если вы наследуете от другого контроллера и хотите переопределить действие от базового контроллера.

ASP.NET MVC - переопределение действия с разными параметрами

Нет, нет и нет. Попробуйте выполнить приведенный ниже код контроллера, где у нас перегружен «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», вы получите ошибку, как показано на рисунке ниже.

Полиморфизм - это часть программирования на 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)».

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

Спасибо за это. Я предполагаю, что вы можете просто использовать другое имя действия с самого начала, а не использовать атрибут.

Dan 05.02.2015 17:35

@Dan, но тогда у нас нет полиморфизма на стороне C#.

Shivprasad Koirala 12.02.2015 09:08

Вы правы, перегрузки метода контроллера нет, но это не имеет отношения к HTTP.

Chalky 15.06.2015 07:46

Спасибо за разъяснение. +1. Следует думать больше о HTTP, а не о C#. Нет причин подходить к действиям с помощью объектно-ориентированной стратегии.

user1017882 12.01.2016 02:50

Я добился этого с помощью Маршрутизация атрибутов в 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)
{
    //.....
}

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

Хорошо сделано! Я просто столкнулся с этой проблемой, вы меня спасли! У меня также есть «x» лет работы с WebForms - так что все еще очень много обучения. Невозможно устроиться на работу без MVC в наши дни, ха-ха

Tez Wingfield 08.09.2016 22:31

Я только что столкнулся с этим вопросом, и, хотя он уже довольно старый, он все еще очень актуален. По иронии судьбы, единственный правильный комментарий в этой ветке был опубликован самопровозглашенным новичком в 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/).

Удачи!

Я тоже столкнулся с такой же проблемой в своем приложении. Без изменения какой-либо информации о методе я указал [ActionName ("SomeMeaningfulName")] в заголовке Action. проблема решена

[ActionName("_EmployeeDetailsByModel")]
        public PartialViewResult _EmployeeDetails(Employee model)
        {
            // Some Operation                
                return PartialView(model);
            }
        }

[ActionName("_EmployeeDetailsByModelWithPagination")]
        public PartialViewResult _EmployeeDetails(Employee model,int Page,int PageSize)
        {

                // Some Operation
                return PartialView(model);

        }

Если это попытка использовать одно действие GET для нескольких представлений, которые отправляют POST для нескольких действий с разными моделями, попробуйте добавить действие GET для каждого действия POST, которое перенаправляет на первый GET, чтобы предотвратить 404 при обновлении.

Длинный выстрел, но обычный сценарий.

Вы можете использовать [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");
    }
}

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

Если вы не желаете использовать разные глаголы (например, атрибуты [HttpGet] и [HttpPost]), чтобы различать перегруженные методы (которые будут работать) или изменять маршрутизацию, то остается то, что вы можете либо предоставить другой метод с другим именем, либо вы можете отправить внутри существующего метода. Вот как я это сделал:

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

Чтобы решить эту проблему, я сделал следующее:

  1. Изменены 2 перегруженных метода действия с общедоступных на частные.
  2. Создал один новый общедоступный метод, содержащий «всего» 2 строковых параметра. Тот выступал диспетчером, то есть:

    public ActionResult DoSomething(string param1, string param2)
    {
        if (string.IsNullOrEmpty(param2))
        {
            return DoSomething(ProductName: param1);
        }
        else
        {
            int oldId = int.Parse(param1);
            return DoSomething(OldParam: param1, OldId: oldId);
        }
    }
    
    
    private ActionResult DoSomething(string OldParam, int OldId)
    {
        // some code here
        return Json(result);
    }
    
    
    private ActionResult DoSomething(string ProductName)
    {
        // some code here
        return Json(result);
    }
    

Конечно, это взлом, и его нужно будет отредактировать позже. Но пока у меня это сработало.

Вы также можете создать диспетчер, например:

public ActionResult DoSomething(string action, string param1, string param2)
{
    switch (action)
    {
        case "update":
            return UpdateAction(param1, param2);
        case "remove":
            return DeleteAction(param1);
    }
}

Как видите, UpdateAction нужно 2 параметра, а DeleteAction - только один.

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

Все кредиты для веб-сайта BinaryIntellect и авторов

По сути, есть четыре ситуации: используя разные глаголы, используя маршрутизацию, маркировка перегрузки атрибутом [NoAction] и измените имя атрибута действия на [ActionName].

Итак, это зависит от ваших требований и вашей ситуации.

Однако перейдите по ссылке:

Ссылка на сайт: http://www.binaryintellect.net/articles/8f9d9a8f-7abf-4df6-be8a-9895882ab562.aspx

Другие вопросы по теме