web-dev-qa-db-fra.com

Pouvez-vous surcharger les méthodes du contrôleur dans ASP.NET MVC?

Je suis curieux de voir si vous pouvez surcharger les méthodes du contrôleur dans ASP.NET MVC. Chaque fois que j'essaie, j'obtiens l'erreur ci-dessous. Les deux méthodes acceptent des arguments différents. Est-ce quelque chose qui ne peut pas être fait?

La demande actuelle d'action 'MyMethod' sur le type de contrôleur 'MyController' est ambiguë entre les méthodes d'action suivantes:

318
Papa Burgundy

Vous pouvez utiliser l'attribut si vous souhaitez que votre code soit surchargé.

[ActionName("MyOverloadedName")]

Mais vous devrez utiliser un nom d'action différent pour la même méthode http (comme d'autres l'ont déjà dit). Donc, c'est juste la sémantique à ce point. Préféreriez-vous avoir le nom dans votre code ou votre attribut?

Phil a publié un article sur ce sujet: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

199
JD Conley

Oui. J'ai pu le faire en définissant l'attribut HttpGet/HttpPost (ou un attribut équivalent AcceptVerbs) pour chaque méthode de contrôleur sur quelque chose de distinct, à savoir HttpGet ou HttpPost, mais pas les deux. De cette façon, il peut déterminer, en fonction du type de demande, la méthode à utiliser.

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

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

Une de mes suggestions est que, pour un cas comme celui-ci, serait d'avoir une implémentation privée sur laquelle vos deux méthodes d'action publiques s'appuient pour éviter la duplication de code.

69
tvanfosson

Voici quelque chose que vous pouvez faire: vous voulez une méthode capable d’avoir un paramètre et non.

Pourquoi ne pas essayer ça ...

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

Cela a fonctionné pour moi ... et avec cette seule méthode, vous pouvez réellement tester pour voir si vous avez le paramètre entrant.


41
Farrel

Non, non et non. Essayez le code du contrôleur ci-dessous, où "LoadCustomer" est surchargé.

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

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

Si vous essayez d'appeler l'action "LoadCustomer", vous obtiendrez une erreur, comme indiqué dans la figure ci-dessous.

enter image description here

Le polymorphisme fait partie de la programmation C # alors que HTTP est un protocole. HTTP ne comprend pas le polymorphisme. HTTP fonctionne sur le concept ou l'URL et l'URL ne peut avoir qu'un nom unique. Donc, HTTP n'implémente pas le polymorphisme.

Afin de résoudre le même problème, nous devons utiliser l'attribut "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");
        }
    }

Alors maintenant, si vous appelez l'URL "Client/LoadCustomer", l'action "LoadCustomer" sera invoquée et avec la structure d'URL "Client/LoadCustomerByName", le "LoadCustomer (string str)" sera invoqué.

enter image description here

enter image description here

La réponse ci-dessus que j'ai tirée de cet article codeproject -> surcharge de l'action MVC

20
Shivprasad Koirala

Pour surmonter ce problème, vous pouvez écrire un ActionMethodSelectorAttribute qui examine le MethodInfo de chaque action et le compare aux valeurs de formulaire publiées. et rejette ensuite toute méthode pour laquelle les valeurs de formulaire ne correspondent pas (en excluant le nom du bouton, bien sûr).

Voici un exemple: - http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

MAIS, ce n'est pas une bonne idée.

15
Ian Mercer

Autant que je sache, vous ne pouvez utiliser la même méthode que si vous utilisez différentes méthodes http.

c'est à dire.

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

}

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

}
13
keeney

J'ai réalisé cela avec l'aide de Attribute Routing dans MVC5. Certes, je suis nouveau chez MVC, issu d'une décennie de développement Web utilisant WebForms, mais les éléments suivants ont fonctionné pour moi. Contrairement à la réponse acceptée, cela permet à toutes les actions surchargées d'être rendues par le même fichier de vue.

Commencez par activer le routage d'attributs dans 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 }
        );            
    }
}

Décorez éventuellement votre classe de contrôleur avec un préfixe de route par défaut.

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

Décorez ensuite les actions de votre contrôleur qui se surchargent les unes des autres avec un itinéraire et des paramètres communs. En utilisant des paramètres de type contrainte, vous pouvez utiliser le même format d'URI avec des identifiants de types différents.

[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)
{
    //.....
}

J'espère que cela aide et ne conduit pas quelqu'un dans la mauvaise voie. :-)

9
cookdn

Je viens de tomber sur cette question et, même si elle est assez ancienne maintenant, elle reste très pertinente. Ironiquement, le seul commentaire correct dans ce fil a été posté par un débutant confiant dans MVC quand il a écrit le post. Même les documents ASP.NET ne sont pas tout à fait corrects. J'ai un gros projet et je surcharge avec succès les méthodes d'action.

Si l'on comprend le routage, au-delà du simple modèle de route par défaut {contrôleur}/{action}/{id}, il peut être évident que les actions du contrôleur peuvent être mappées à l'aide de n'importe quel modèle unique. Quelqu'un ici a parlé de polymorphisme et a déclaré: "HTTP ne comprend pas le polymorphisme", mais le routage n'a rien à voir avec HTTP. En termes simples, il s’agit d’un mécanisme de correspondance des modèles de chaînes.

La meilleure façon de procéder consiste à utiliser les attributs de routage, par exemple:

[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
    }
}

Ces actions prendront en charge des URL telles que /cars/usa/new-york et /cars/usa/texas/dallas, qui mapperont les première et deuxième actions d'index, respectivement.

En examinant cet exemple de contrôleur, il est évident qu'il va au-delà du modèle de route par défaut mentionné ci-dessus. La valeur par défaut fonctionne bien si votre structure d'URL correspond exactement aux conventions de nommage de votre code, mais ce n'est pas toujours le cas. Le code doit être descriptif du domaine, mais les URL doivent souvent aller plus loin car leur contenu doit être basé sur d'autres critères, tels que les exigences de référencement.

L'avantage du modèle de routage par défaut est qu'il crée automatiquement des itinéraires uniques. Ceci est imposé par le compilateur puisque les URL correspondront à des types de contrôleurs et à des membres uniques. Faire rouler vos propres itinéraires nécessitera une réflexion approfondie pour garantir leur unicité et leur efficacité.

Remarque importante L'inconvénient est que l'utilisation du routage pour générer des URL pour les actions surchargées ne fonctionne pas si elle est basée sur un nom d'action, par exemple lorsque vous utilisez UrlHelper.Action. . Mais cela fonctionne si on utilise des itinéraires nommés, par exemple, UrlHelper.RouteUrl. Et utiliser des itinéraires nommés est, selon des sources respectées, la voie à suivre de toute façon ( http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/ ).

Bonne chance!

4
DvS

Vous pouvez utiliser un seul ActionResult pour traiter à la fois Post et Get:

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

Utile si vos méthodes Get et Post ont des signatures correspondantes.

4
DevDave

Vous pouvez utiliser [ActionName ("NewActionName")] pour utiliser la même méthode avec un nom différent:

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");
    }
}
3
user6685907

J'avais besoin d'une surcharge pour:

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

Il y avait peu d'arguments assez où j'ai fini par faire ceci:

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
    }
}

Ce n'est pas une solution parfaite, surtout si vous avez beaucoup d'arguments, mais cela fonctionne bien pour moi.

2
Kasey Speakman

J'ai également rencontré le même problème dans ma candidature. Sans modifier aucune information de méthode, j'ai fourni [ActionName ("SomeMeaningfulName")] sur la tête d'action. problème résolu

[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);

        }
1
ಅನಿಲ್

Une seule signature publique est autorisée pour chaque méthode de contrôleur. Si vous essayez de le surcharger, il se compilera, mais vous obtenez l'erreur d'exécution que vous avez rencontrée.

Si vous ne souhaitez pas utiliser différents verbes (tels que les attributs [HttpGet] et [HttpPost]) pour différencier les méthodes surchargées (ce qui fonctionnera), ou modifier le routage, il vous reste alors à fournir une autre méthode avec un nom différent, ou vous pouvez envoyer à l’intérieur de la méthode existante. Voici comment je l'ai fait:

Une fois, je me suis retrouvé dans une situation où je devais maintenir une compatibilité ascendante. La méthode originale prévoyait deux paramètres, mais le nouveau n'en avait qu'un. La surcharge telle que prévue ne fonctionnait pas car MVC ne trouvait plus le point d’entrée.

Pour résoudre cela, j'ai fait ce qui suit:

  1. Changement des 2 méthodes d'action surchargées du public au privé
  2. Création d'une nouvelle méthode publique contenant "seulement" 2 paramètres de chaîne. Celui-ci a agi en tant que répartiteur, à savoir:

    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);
    }
    

Bien sûr, ceci est un hack et devrait être refait plus tard. Mais pour le moment, cela a fonctionné pour moi.

Vous pouvez également créer un répartiteur tel que:

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

Vous pouvez voir que UpdateAction a besoin de 2 paramètres, alors que DeleteAction en a juste besoin.

0
Matt

Créer la méthode de base en tant que virtuelle

public virtual ActionResult Index()

Créer la méthode substituée en tant que substitution

public override ActionResult Index()

Edit: Ceci ne s'applique évidemment que si la méthode de substitution est dans une classe dérivée qui semble ne pas avoir été l'intention du PO.

0
Andiih

Désolé pour le retard. J'étais avec le même problème et j'ai trouvé un lien avec de bonnes réponses, cela pourrait-il aider les nouveaux gars

Tous les crédits du site Web BinaryIntellect et des auteurs

En gros, il existe quatre situations: en utilisant différents verbes , en utilisant le routage , et le marquage de surcharge avec [NoAction ] attribut et modifient le nom de l'attribut d'action avec [NomAction]

Cela dépend donc de vos exigences et de votre situation.

Quoi qu'il en soit, suivez le lien:

Lien: http://www.binaryintellect.net/articles/8f9d9a8f-7abf-4df6-be8a-9895882ab562.aspx

0
Eric Saboia

J'aime cette réponse postée dans un autre fil

Ceci est principalement utilisé si vous héritez d'un autre contrôleur et souhaitez remplacer une action du contrôleur de base

ASP.NET MVC - Remplacement d'une action avec des paramètres différents

0
Keyjote