Domanda

Sono curioso di vedere se è possibile sovraccaricare i metodi del controller in ASP.NET MVC. Ogni volta che provo, visualizzo l'errore di seguito. I due metodi accettano argomenti diversi. È qualcosa che non si può fare?

  

L'attuale richiesta di azione "MyMethod" sul tipo di controller "MyController" è ambigua tra i seguenti metodi di azione:

È stato utile?

Soluzione

Puoi usare l'attributo se vuoi che il tuo codice sovraccarichi.

[ActionName("MyOverloadedName")]

Ma dovrai usare un nome di azione diverso per lo stesso metodo http (come altri hanno già detto). Quindi è solo la semantica a quel punto. Preferiresti avere il nome nel tuo codice o nel tuo attributo?

Phil ha un articolo correlato a questo: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

Altri suggerimenti

Sì. Sono stato in grado di farlo impostando l'attributo HttpGet / HttpPost (o l'attributo AcceptVerbs equivalente) per ciascun metodo del controller su qualcosa di distinto, ad es. , HttpGet o HttpPost , ma non entrambi. In questo modo può stabilire in base al tipo di richiesta quale metodo utilizzare.

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

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

Un suggerimento che ho è che, per un caso come questo, sarebbe di avere un'implementazione privata su cui entrambi i tuoi metodi di azione pubblica si basano per evitare la duplicazione del codice.

Ecco qualcos'altro che potresti fare ... vuoi un metodo che sia in grado di avere un parametro e non.

Perché non provare questo ...

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

Questo ha funzionato per me ... e in questo unico metodo, puoi effettivamente testare per vedere se hai il parametro in arrivo.


Aggiornato per rimuovere la sintassi nullable non valida sulla stringa e utilizzare un valore di parametro predefinito.

No, No e No. Vai e prova il codice controller qui sotto dove abbiamo il "LoadCustomer" sovraccarico.

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

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

Se provi a invocare il " LoadCustomer " si otterrà un errore, come mostrato nella figura seguente.

inserisci qui la descrizione dell'immagine

Il polimorfismo fa parte della programmazione C # mentre HTTP è un protocollo. HTTP non capisce il polimorfismo. HTTP funziona sul concetto o sull'URL e l'URL può avere solo nomi univoci. Quindi HTTP non implementa il polimorfismo.

Per risolvere lo stesso dobbiamo usare " ActionName " attributo.

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

Quindi ora se fai una chiamata all'URL " Customer / LoadCustomer " il " LoadCustomer " l'azione verrà invocata e con la struttura dell'URL " Customer / LoadCustomerByName " il " LoadCustomer (string str) " sarà invocato.

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

La risposta di cui sopra ho preso da questo articolo codeproject - > Sovraccarico dell'azione MVC

Per ovviare a questo problema puoi scrivere un ActionMethodSelectorAttribute che esamina il MethodInfo per ogni azione e lo confronta con i valori del modulo pubblicato e quindi rifiuta qualsiasi metodo per il quale i valori del modulo non corrispondono (escluso ovviamente il nome del pulsante).

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

MA, questa non è una buona idea.

Per quanto ne so, puoi avere lo stesso metodo solo quando usi metodi http diversi.

cioè.

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

}

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

}

L'ho raggiunto con l'aiuto di Instradamento degli attributi in MVC5. Devo ammettere che sono una novità di MVC proveniente da un decennio di sviluppo web con WebForms, ma per me ha funzionato. A differenza della risposta accettata, ciò consente di eseguire il rendering di tutte le azioni sovraccaricate dallo stesso file di visualizzazione.

Abilita prima il routing degli attributi in 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 }
        );            
    }
}

Facoltativamente, decorare la classe del controller con un prefisso di route predefinito.

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

Quindi decorare le azioni del controller che si sovraccaricano a vicenda con un percorso comune e parametri adeguati. Utilizzando parametri con vincolo di tipo è possibile utilizzare lo stesso formato URI con ID di tipi diversi.

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

Spero che questo aiuti e non porti qualcuno sulla strada sbagliata. : -)

Puoi utilizzare un singolo ActionResult per gestire sia Post sia Get :

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

Utile se i tuoi metodi Get e Post hanno firme corrispondenti.

Mi sono appena imbattuto in questa domanda e, anche se ora è piuttosto vecchio, è ancora molto rilevante. Ironia della sorte, l'unico commento corretto in questa discussione è stato pubblicato da un principiante confesso in MVC quando ha scritto il post. Anche i documenti ASP.NET non sono completamente corretti. Ho un grande progetto e sovraccarico con successo i metodi di azione.

Se si comprende il routing, oltre al semplice modello di route predefinito {controller} / {action} / {id}, potrebbe essere ovvio che le azioni del controller possono essere mappate utilizzando qualsiasi modello univoco. Qualcuno qui ha parlato di polimorfismo e ha detto: "HTTP non capisce il polimorfismo", ma il routing non ha nulla a che fare con HTTP. È, in poche parole, un meccanismo per la corrispondenza del modello di stringa.

Il modo migliore per farlo funzionare è utilizzare gli attributi di routing, ad esempio:

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

Queste azioni si occuperanno di URL come / cars / usa / new-york e / cars / usa / texas / dallas , che verranno mappati al primo e azioni secondo indice rispettivamente.

L'esame di questo controller di esempio è evidente che va oltre il modello di route predefinito menzionato sopra. L'impostazione predefinita funziona bene se la struttura dell'URL corrisponde esattamente alle convenzioni di denominazione del codice, ma non è sempre così. Il codice dovrebbe essere descrittivo del dominio, ma spesso gli URL devono andare oltre perché il loro contenuto dovrebbe essere basato su altri criteri, come i requisiti SEO.

Il vantaggio del modello di routing predefinito è che crea automaticamente percorsi univoci. Ciò viene applicato dal compilatore poiché gli URL corrisponderanno a tipi e membri di controller univoci. Far rotolare i propri schemi di percorso richiederà un'attenta riflessione per garantire l'unicità e il loro funzionamento.

Nota importante L'unico inconveniente è che l'uso del routing per generare URL per azioni sovraccariche non funziona quando si basa su un nome di azione, ad esempio quando si utilizza UrlHelper.Action. Ma funziona se si utilizzano route denominate, ad esempio UrlHelper.RouteUrl. E usare percorsi con nome è, secondo fonti rispettate, la strada da percorrere in ogni caso ( http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/ ).

Buona fortuna!

Puoi utilizzare [ActionName (" NewActionName ")] per utilizzare lo stesso metodo con un nome diverso:

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

Avevo bisogno di un sovraccarico per:

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

C'erano pochi argomenti sufficienti in cui ho finito per farlo:

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

Non è una soluzione perfetta, soprattutto se hai molti argomenti, ma funziona bene per me.

Ho riscontrato lo stesso problema anche nella mia applicazione. Senza modificare alcuna informazione sul metodo, ho fornito [ActionName (" SomeMeaningfulName ")] su Head azione. problema risolto

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

        }

Crea il metodo di base come virtuale

public virtual ActionResult Index()

Crea il metodo override come override

public override ActionResult Index()

Modifica: questo ovviamente si applica solo se il metodo override è in una classe derivata che sembra non essere stata l'intenzione del PO.

Mi piace questa risposta pubblicata in un'altra discussione

Viene utilizzato principalmente se si eredita da un altro controller e si desidera sovrascrivere un'iscrizione dal controller di base

ASP.NET MVC - Sostituzione di un'azione con parametri diversi

È consentita una sola firma pubblica per ciascun metodo del controller. Se si tenta di sovraccaricarlo, verrà compilato, ma viene visualizzato l'errore di runtime che si è verificato.

Se non sei disposto a utilizzare diversi verbi (come gli attributi [HttpGet] e [HttpPost] ) per differenziare i metodi sovraccarichi (che funzioneranno), oppure modificare il routing, quindi ciò che rimane è che è possibile fornire un altro metodo con un nome diverso o è possibile inviare all'interno del metodo esistente. Ecco come l'ho fatto:

Una volta sono entrato in una situazione in cui dovevo mantenere la retrocompatibilità. Il metodo originale prevedeva due parametri, ma il nuovo aveva solo uno. Il sovraccarico come mi aspettavo non funzionava perché MVC non trovava più il punto di ingresso.

Per risolverlo, ho fatto quanto segue:

  1. Modificati i 2 metodi di azione sovraccarichi da pubblico a privato
  2. Creato un nuovo metodo pubblico che conteneva " solo " 2 parametri di stringa. Quello ha agito come dispatcher, ovvero:

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

Ovviamente, questo è un trucco e dovrebbe essere modificato in seguito. Ma per il momento, ha funzionato per me.

Puoi anche creare un dispatcher come:

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

Puoi vedere che UpdateAction ha bisogno di 2 parametri, mentre DeleteAction ne ha solo bisogno.

Se si tratta di un tentativo di utilizzare un'azione GET per più viste che POST a più azioni con modelli diversi, provare ad aggiungere un'azione GET per ciascuna azione POST che reindirizza al primo GET per impedire l'aggiornamento 404.

Long shot ma scenario comune.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top