c# - net - asp-route
¿Se pueden sobrecargar los métodos del controlador en ASP.NET MVC? (16)
Tengo curiosidad por ver si puede sobrecargar los métodos del controlador en ASP.NET MVC. Cada vez que lo intento, me sale el error de abajo. Los dos métodos aceptan diferentes argumentos. ¿Es esto algo que no se puede hacer?
La solicitud actual de acción ''MyMethod'' en el tipo de controlador ''MyController'' es ambigua entre los siguientes métodos de acción:
Acabo de encontrar esta pregunta y, aunque ya es bastante antigua, sigue siendo muy relevante. Irónicamente, el comentario correcto en este hilo fue publicado por un principiante confesado en MVC cuando escribió la publicación. Incluso los documentos de ASP.NET no son del todo correctos. Tengo un gran proyecto y sobrecargo con éxito los métodos de acción.
Si uno entiende el enrutamiento, más allá del patrón de ruta predeterminado simple {controller} / {action} / {id}, podría ser obvio que las acciones del controlador se pueden asignar mediante un patrón único. Alguien aquí habló sobre el polimorfismo y dijo: "HTTP no entiende el polimorfismo", pero el enrutamiento no tiene nada que ver con HTTP. Es, simplemente, un mecanismo para la coincidencia de patrones de cadena.
La mejor manera de hacer que esto funcione es usar los atributos de enrutamiento, por ejemplo:
[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
}
}
Estas acciones se ocuparán de direcciones URL como /cars/usa/new-york
y /cars/usa/texas/dallas
, que se asignarán a la primera y la segunda acción del índice, respectivamente.
Al examinar este controlador de ejemplo, es evidente que va más allá del patrón de ruta predeterminado mencionado anteriormente. El valor predeterminado funciona bien si su estructura de URL coincide exactamente con sus convenciones de nomenclatura de códigos, pero no siempre es así. El código debe ser descriptivo del dominio, pero las direcciones URL a menudo deben ir más lejos porque su contenido debe basarse en otros criterios, como los requisitos de SEO.
La ventaja del patrón de enrutamiento predeterminado es que crea automáticamente rutas únicas. El compilador impone esto, ya que las direcciones URL coincidirán con los tipos de controladores y miembros únicos. Hacer rodar sus propios patrones de ruta requerirá una reflexión cuidadosa para asegurar la singularidad y que funcionen.
Nota importante El único inconveniente es que el uso de enrutamiento para generar URL para acciones sobrecargadas no funciona cuando se basa en un nombre de acción, por ejemplo, cuando se utiliza UrlHelper.Action. Pero funciona si uno usa rutas con nombre, por ejemplo, UrlHelper.RouteUrl. Y el uso de rutas con nombre es, de acuerdo con fuentes bien respetadas, el camino a seguir ( http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/ ).
¡Buena suerte!
Aquí hay algo más que podría hacer ... quiere un método que sea capaz de tener un parámetro y no.
¿Por qué no intentar esto ...
public ActionResult Show( string username = null )
{
...
}
Esto me ha funcionado ... y con este método, puedes probar si tienes el parámetro entrante.
Actualizado para eliminar la sintaxis anulable no válida en la cadena y usar un valor de parámetro predeterminado.Crea el método base como virtual.
public virtual ActionResult Index()
Crear el método anulado como anulación
public override ActionResult Index()
Edición: esto obviamente se aplica solo si el método de anulación está en una clase derivada que parece no haber sido la intención del OP.
He enfrentado el mismo problema en mi aplicación también. Sin modificar ningún tipo de información sobre el método, proporcioné [ActionName ("SomeMeaningfulName")] en el encabezado de Action. problema resuelto
[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);
}
Lo he logrado con la ayuda del enrutamiento de atributos en MVC5. Es cierto que soy nuevo en MVC proveniente de una década de desarrollo web con WebForms, pero lo siguiente me ha funcionado. A diferencia de la respuesta aceptada, esto permite que todas las acciones sobrecargadas se representen en el mismo archivo de vista.
Primero habilite el enrutamiento de atributos en 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 }
);
}
}
Opcionalmente, decore su clase de controlador con un prefijo de ruta predeterminado.
[RoutePrefix("Returns")]
public class ReturnsController : BaseController
{
//.......
Luego, decora las acciones de tu controlador que se sobrecargan entre sí con una ruta y parámetros comunes que se ajusten. Usando parámetros de tipo restringido, puede usar el mismo formato URI con ID de diferentes tipos.
[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)
{
//.....
}
Espero que esto ayude y no guíe a alguien por el camino equivocado. :-)
Me gusta esta respuesta publicada en otro hilo.
Esto se usa principalmente si hereda de otro controlador y desea anular una acción del controlador base
Necesitaba una sobrecarga para:
public ActionResult Index(string i);
public ActionResult Index(int groupId, int itemId);
Hubo pocos argumentos donde terminé haciendo esto:
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
}
}
No es una solución perfecta, especialmente si tiene muchos argumentos, pero funciona bien para mí.
No, No y No. Vaya y pruebe el código del controlador a continuación donde tenemos el "LoadCustomer" sobrecargado.
public class CustomerController : Controller
{
//
// GET: /Customer/
public ActionResult LoadCustomer()
{
return Content("LoadCustomer");
}
public ActionResult LoadCustomer(string str)
{
return Content("LoadCustomer with a string");
}
}
Si intenta invocar la acción "Cargar cliente", obtendrá un error, como se muestra en la siguiente figura.
El polimorfismo es una parte de la programación de C #, mientras que HTTP es un protocolo. HTTP no entiende el polimorfismo. HTTP funciona en el concepto o URL y la URL solo puede tener nombres únicos. Así que HTTP no implementa polimorfismo.
Para arreglar el mismo necesitamos usar el atributo "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");
}
}
Así que ahora, si realiza una llamada a la URL "Customer / LoadCustomer", se invocará la acción "LoadCustomer" y con la estructura de URL "Customer / LoadCustomerByName" se invocará a "LoadCustomer (string str)".
La respuesta anterior he tomado de este artículo de código de proyecto -> Sobrecarga de acción MVC
Para superar este problema, puede escribir un ActionMethodSelectorAttribute
que examine el MethodInfo
para cada acción y lo compare con los valores del formulario publicado y luego rechace cualquier método para el cual los valores del formulario no coincidan (excluyendo el nombre del botón, por supuesto).
Aquí hay un ejemplo: - http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/
PERO, esto no es una buena idea.
Podría usar un único ActionResult
para tratar tanto con Post
como Get
:
public ActionResult Example() {
if (Request.HttpMethod.ToUpperInvariant() == "GET") {
// GET
}
else if (Request.HttpMethod.ToUpperInvariant() == "POST") {
// Post
}
}
Útil si sus métodos Get
y Post
tienen firmas coincidentes.
Por lo que sé, solo se puede tener el mismo método cuando se utilizan diferentes métodos http.
es decir
[AcceptVerbs("GET")]
public ActionResult MyAction()
{
}
[AcceptVerbs("POST")]
public ActionResult MyAction(FormResult fm)
{
}
Puede usar [ActionName ("NewActionName")] para usar el mismo método con un nombre diferente:
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");
}
}
Puede usar el atributo si desea que su código realice una sobrecarga.
[ActionName("MyOverloadedName")]
Pero tendrá que usar un nombre de acción diferente para el mismo método http (como han dicho otros). Así que es solo semántica en ese punto. ¿Prefieres tener el nombre en tu código o tu atributo?
Phil tiene un artículo relacionado con esto: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx
Sí. He podido hacer esto configurando el HttpGet
/ HttpPost
(o el atributo AcceptVerbs
equivalente) para cada método del controlador en algo distinto, es decir, HttpGet
o HttpPost
, pero no ambos. De esa manera, puede determinar en función del tipo de solicitud qué método utilizar.
[HttpGet]
public ActionResult Show()
{
...
}
[HttpPost]
public ActionResult Show( string userName )
{
...
}
Una sugerencia que tengo es que, para un caso como este, sería tener una implementación privada en la que ambos métodos públicos de Acción se basen para evitar la duplicación de código.
Sólo se permite una firma pública para cada método de controlador. Si intenta sobrecargarlo, se compilará, pero obtendrá el error de tiempo de ejecución que ha experimentado.
Si no está dispuesto a usar verbos diferentes (como los [HttpGet]
y [HttpPost]
) para diferenciar los métodos sobrecargados (que funcionarán), o cambiar la ruta, entonces lo que queda es que puede proporcionar otro método con una nombre diferente, o puede enviar dentro del método existente. Así es como lo hice:
Una vez llegué a una situación en la que tenía que mantener la compatibilidad hacia atrás. El método original esperaba dos parámetros, pero el nuevo solo tenía uno. La sobrecarga de la forma que esperaba no funcionó porque MVC ya no encontró el punto de entrada.
Para resolver eso, hice lo siguiente:
- Se modificaron los 2 métodos de acción sobrecargados de público a privado.
Creó un nuevo método público que contenía "solo" 2 parámetros de cadena. Ese actuó como un despachador, es decir:
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); }
Por supuesto, esto es un truco y debería ser refactorizado más tarde. Pero por el momento, funcionó para mí.
También puede crear un despachador como:
public ActionResult DoSomething(string action, string param1, string param2)
{
switch (action)
{
case "update":
return UpdateAction(param1, param2);
case "remove":
return DeleteAction(param1);
}
}
Puedes ver que UpdateAction necesita 2 parámetros, mientras que DeleteAction solo necesita uno.
Si se trata de un intento de usar una acción GET para varias vistas que POST a varias acciones con diferentes modelos, intente agregar una acción GET para cada acción POST que redirija al primer GET para evitar la actualización de 404.
Plano general pero muy largo.