Мне любопытно узнать, можно ли перегрузить методы контроллера в ASP.NET MVC. Всякий раз, когда я пытаюсь, я получаю сообщение об ошибке ниже. Эти два метода принимают разные аргументы. Это то, чего нельзя сделать?
The current request for action 'MyMethod' on controller type 'MyController' is ambiguous between the following action methods:
И то же самое для mvc 5
И то же самое для mvc 6
То же самое для MVC Core 1.1
То же самое для MVC Core 2.0
Кто знает, может быть, для MVC Core 3.0 все по-другому [это не]





Насколько я знаю, у вас может быть только один и тот же метод при использовании разных методов http.
т.е.
[AcceptVerbs("GET")]
public ActionResult MyAction()
{
}
[AcceptVerbs("POST")]
public ActionResult MyAction(FormResult fm)
{
}
украшения не имеют никакого отношения к перегрузке. это список параметров, допускающих перегрузку.
@SkySanders Я не согласен, перегрузка на основе параметров не работает в методах контроллера MVC - у вас есть рабочий пример? Ваше здоровье.
Используйте атрибут [HttpPost] вместо [AcceptVerbs("POST")].
Да. Я смог сделать это, установив для атрибута HttpGet / HttpPost (или эквивалентного атрибута AcceptVerbs) для каждого метода контроллера нечто особенное, например, HttpGet или HttpPost, но не оба сразу. Таким образом, в зависимости от типа запроса он может определить, какой метод использовать.
[HttpGet]
public ActionResult Show()
{
...
}
[HttpPost]
public ActionResult Show( string userName )
{
...
}
Одно из моих предложений заключается в том, что для такого случая нужно иметь частную реализацию, на которую полагаются оба ваших общедоступных метода Action, чтобы избежать дублирования кода.
С MVC2 и выше можно также использовать атрибут HttpPost / HttpGet.
@yohal Да, это был бы канонический способ справиться с этим сейчас, если вам не нужно поддерживать несколько глаголов.
Только будьте осторожны, не злоупотребляйте этим, чтобы не нарушать принципы REST.
Совершенно уверен, что это работает только потому, что ваши методы Show() имеют разные сигнатуры. Если и когда вам нужно отправить информацию в версию Get, ваши версии Get и Post будут иметь одинаковую подпись, и вам понадобится атрибут ActionName или одно из других исправлений, упомянутых в этом сообщении.
@ ScottK.Fraley, это правда. Если им нужна одна и та же подпись, вам придется назвать их по-другому и применить ActionNameAttribute. На практике я редко обнаруживал, что это так.
Я случайно нашел эту статью / сообщение, потому что это проблема, с которой я столкнулся прямо сейчас, и фактически в нескольких местах моего текущего проекта. Я согласен, что это редкость. :)
При переименовании имен действий у меня случайно было одно из действий, украшенное атрибутом [Route ("xxxx")], имя которого совпадало с именем действия. Остерегайтесь этого, так как это испортит вашу попытку перегрузки.
Вы можете использовать атрибут, если хотите, чтобы ваш код выполнял перегрузку.
[ActionName("MyOverloadedName")]
Но вам придется использовать другое имя действия для того же метода http (как говорили другие). Так что это просто семантика. Вы бы предпочли, чтобы имя было в вашем коде или в атрибуте?
У Фила есть статья по этому поводу: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx
Главный недостаток использования этого и перегрузки вашего действия заключается в том, что он больше не может быть обработан одним и тем же файлом представления.
Фактически, он все еще может отображать тот же файл представления. Вам просто нужно указать имя представления вместо того, чтобы вслепую вызывать return View();. Например: return View("MyOverloadedName");.
@JD, но Microsoft говорит .. Метод, используемый в качестве действия контроллера, не может быть перегружен .. Вы можете увидеть это здесь ..asp.net/mvc/tutorials/controllers-and-routing/…
@EAMann Приятно, до сих пор я всегда определял весь путь для представления
Чтобы решить эту проблему, вы может пишете ActionMethodSelectorAttribute, который проверяет MethodInfo для каждого действия и сравнивает его с опубликованными значениями формы, а затем отклоняет любой метод, для которого значения формы не совпадают (за исключением, конечно, имени кнопки).
Вот пример: - http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/
НО, это плохая идея.
@Cerbrus, потому что это ужасный взлом, и следующий человек, который посмотрит на код вашего контроллера, будет сбит с толку очень нестандартным подходом.
Хех, достаточно честно.
Вот еще кое-что, что вы могли бы сделать ... вам нужен метод, который может иметь параметр, а не.
Почему бы не попробовать это ...
public ActionResult Show( string username = null )
{
...
}
Это сработало для меня ... и в этом методе вы можете проверить, есть ли у вас входящий параметр.
(string не может иметь значение NULL.)
строка может допускать значение NULL. Фактически, он уже имеет значение NULL, просто не нужно '?'
@ProfK - Нет, строка - это ссылочный тип, который может быть нулевым. Это не "обнуляемое". Nullable означает, что вы используете Nullable <T> (т.е. T?). Суть Джоша в том, что вы не можете поставить? после строки, потому что это не тип значения, а Nullable <T> принимает только типы значений.
Я случайно нашел способ вернуться к этому вопросу, а затем понял, что опубликовал комментарий выше. Не припомню этого ... странного! По-прежнему верно, что string не может быть nullable; но это может быть null! Так или иначе, я опубликовал первоначальный комментарий без искренности.
Создайте базовый метод как виртуальный
public virtual ActionResult Index()
Создайте переопределенный метод как переопределение
public override ActionResult Index()
Обновлено: это, очевидно, применяется только в том случае, если метод переопределения находится в производном классе, который, похоже, не был намерением OP.
Вы, наверное, неправильно поняли вопрос. OP запрашивает перегрузку метода в том же контроллере, а не переопределение его в производном классе.
@Andiih: что произойдет, если оба метода находятся в одном контроллере?
Вы можете использовать один ActionResult для работы как с Post, так и с Get:
public ActionResult Example() {
if (Request.HttpMethod.ToUpperInvariant() == "GET") {
// GET
}
else if (Request.HttpMethod.ToUpperInvariant() == "POST") {
// Post
}
}
Полезно, если ваши методы Get и Post имеют совпадающие сигнатуры.
Хм, вроде - снова изобретаю колесо, но на этот раз квадратной формы. Почему бы просто не использовать атрибуты [HttpPost / Get]?
это было давно, но я думаю, что сделал это, потому что MVC не различал два отдельных метода с соответствующими знаками. Я использовал атрибут HttpPost, но не использовал HttpGet для другого метода.
@DevDave, а также приписывая оба метода, убедитесь, что вы используете атрибуты из system.web.mvc, а не из system.web.http!
Мне нужна была перегрузка для:
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, но тогда у нас нет полиморфизма на стороне C#.
Вы правы, перегрузки метода контроллера нет, но это не имеет отношения к HTTP.
Спасибо за разъяснение. +1. Следует думать больше о HTTP, а не о C#. Нет причин подходить к действиям с помощью объектно-ориентированной стратегии.
Я добился этого с помощью Маршрутизация атрибутов в 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 в наши дни, ха-ха
Я только что столкнулся с этим вопросом, и, хотя он уже довольно старый, он все еще очень актуален. По иронии судьбы, единственный правильный комментарий в этой ветке был опубликован самопровозглашенным новичком в 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 больше не находил точку входа.
Чтобы решить эту проблему, я сделал следующее:
Создал один новый общедоступный метод, содержащий «всего» 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
@andy то же самое и для mvc 4 :)