it-swarm.com.de

Können Sie Controller-Methoden in ASP.NET MVC überladen?

Ich bin gespannt, ob Sie Controller-Methoden in ASP.NET MVC überladen können. Wann immer ich es versuche, erhalte ich den folgenden Fehler. Die beiden Methoden akzeptieren unterschiedliche Argumente. Kann man das nicht machen?

Die aktuelle Anforderung der Aktion 'MyMethod' für den Controllertyp 'MyController' ist zwischen den folgenden Aktionsmethoden nicht eindeutig:

318
Papa Burgundy

Sie können das Attribut verwenden, wenn der Code überladen werden soll.

[ActionName("MyOverloadedName")]

Sie müssen jedoch einen anderen Aktionsnamen für dieselbe http-Methode verwenden (wie bereits erwähnt). Zu diesem Zeitpunkt ist es also nur Semantik. Möchten Sie lieber den Namen in Ihrem Code oder Ihrem Attribut haben?

Phil hat einen Artikel dazu: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

199
JD Conley

Ja. Ich konnte dies tun, indem ich das HttpGet/HttpPost (oder das entsprechende AcceptVerbs-Attribut) für jede Controllermethode auf etwas anderes einstellte, dh HttpGet oder HttpPost, aber nicht beide. Auf diese Weise kann anhand des Anfragetyps festgestellt werden, welche Methode verwendet werden soll.

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

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

Ein Vorschlag, den ich habe, ist, dass in einem Fall wie diesem eine private Implementierung vorhanden sein sollte, auf die sich beide öffentlichen Aktionsmethoden stützen, um das Duplizieren von Code zu vermeiden.

69
tvanfosson

Hier ist noch etwas, was Sie tun könnten ... Sie möchten eine Methode, die einen Parameter haben kann und nicht.

Warum versuchst du das nicht ...

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

Das hat bei mir funktioniert ... und bei dieser einen Methode können Sie tatsächlich testen, ob Sie den eingehenden Parameter haben.


41
Farrel

Nein, Nein und Nein. Probieren Sie den folgenden Controller-Code aus, in dem der "LoadCustomer" überladen ist.

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

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

Wenn Sie versuchen, die Aktion "LoadCustomer" aufzurufen, wird der in der folgenden Abbildung gezeigte Fehler angezeigt.

enter image description here

Polymorphismus ist ein Teil der C # -Programmierung, während HTTP ein Protokoll ist. HTTP versteht keinen Polymorphismus. HTTP funktioniert mit dem Konzept oder URL und URL kann nur eindeutige Namen haben. HTTP implementiert also keinen Polymorphismus.

Um dies zu beheben, müssen wir das Attribut "ActionName" verwenden.

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

Wenn Sie also die URL "Customer/LoadCustomer" aufrufen, wird die Aktion "LoadCustomer" aufgerufen und mit der URL-Struktur "Customer/LoadCustomerByName" wird die Aktion "LoadCustomer (string str)" aufgerufen.

enter image description here

enter image description here

Die obige Antwort habe ich aus diesem Code-Projekt-Artikel entnommen -> MVC Action Overloading

20

Um dieses Problem zu lösen, können Sie ein ActionMethodSelectorAttribute schreiben , das das MethodInfo für jede Aktion untersucht und mit dem veröffentlichten vergleicht Formularwerte und lehnt dann alle Methoden ab, für die die Formularwerte nicht übereinstimmen (natürlich mit Ausnahme des Schaltflächennamens).

Hier ist ein Beispiel: - http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

ABER das ist keine gute Idee.

15
Ian Mercer

Soweit ich weiß, können Sie dieselbe Methode nur verwenden, wenn Sie verschiedene http-Methoden verwenden.

d.h.

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

}

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

}
13
keeney

Ich habe dies mit Hilfe von Attribute Routing in MVC5 erreicht. Ich bin zwar neu bei MVC und komme aus einem Jahrzehnt der Webentwicklung mit WebForms, aber das Folgende hat für mich funktioniert. Im Gegensatz zur akzeptierten Antwort können auf diese Weise alle überladenen Aktionen von derselben Ansichtsdatei gerendert werden.

Aktivieren Sie zuerst das Attributrouting 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 }
        );            
    }
}

Optional können Sie Ihre Controller-Klasse mit einem Standard-Routenpräfix ausstatten.

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

Dekorieren Sie dann Ihre Controller-Aktionen, die sich gegenseitig mit einer gemeinsamen Route und passenden Parametern überladen. Mit typbeschränkten Parametern können Sie dasselbe URI-Format mit IDs verschiedener Typen verwenden.

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

Ich hoffe, das hilft und führt niemanden auf den falschen Weg. :-)

9
cookdn

Ich bin gerade auf diese Frage gestoßen und obwohl sie jetzt ziemlich alt ist, ist sie immer noch sehr relevant. Ironischerweise wurde der eine richtige Kommentar in diesem Thread von einem selbstbekannten Anfänger in MVC gepostet, als er den Beitrag schrieb. Sogar die ASP.NET-Dokumente sind nicht ganz korrekt. Ich habe ein großes Projekt und überlade erfolgreich Aktionsmethoden.

Wenn man das Routing über das einfache {controller}/{action}/{id} -Standardroutenmuster hinaus versteht, ist es möglicherweise offensichtlich, dass Controlleraktionen mithilfe eines eindeutigen Musters zugeordnet werden können. Jemand hier sprach über Polymorphismus und sagte: "HTTP versteht Polymorphismus nicht", aber Routing hat nichts mit HTTP zu tun. Es ist einfach ausgedrückt ein Mechanismus für den String-Mustervergleich.

Der beste Weg, dies zu erreichen, ist die Verwendung der Routing-Attribute, zum Beispiel:

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

Diese Aktionen kümmern sich um URLs wie /cars/usa/new-york und /cars/usa/texas/dallas, das der ersten bzw. der zweiten Indexaktion zugeordnet wird.

Betrachtet man diesen beispielhaften Controller, so ist klar, dass er über das oben erwähnte Standardroutenmuster hinausgeht. Der Standard funktioniert gut, wenn Ihre URL-Struktur genau Ihren Code-Namenskonventionen entspricht, dies ist jedoch nicht immer der Fall. Der Code sollte die Domain beschreiben, aber die URLs müssen oft weiter gehen, da ihr Inhalt auf anderen Kriterien basieren sollte, wie z. B. SEO-Anforderungen.

Der Vorteil des Standard-Routing-Musters besteht darin, dass automatisch eindeutige Routen erstellt werden. Dies wird vom Compiler erzwungen, da URLs eindeutigen Controllertypen und -mitgliedern entsprechen. Das Rollen Ihrer eigenen Routenmuster erfordert sorgfältige Überlegungen, um die Eindeutigkeit sicherzustellen und um sicherzustellen, dass sie funktionieren.

Wichtiger Hinweis Der einzige Nachteil ist, dass die Verwendung von Routing zum Generieren von URLs für überladene Aktionen nicht funktioniert, wenn ein Aktionsname verwendet wird, z. B. bei Verwendung von UrlHelper.Action . Es funktioniert jedoch, wenn benannte Routen verwendet werden, z. B. "UrlHelper.RouteUrl". Die Verwendung benannter Routen ist nach allgemein anerkannten Quellen auf jeden Fall der richtige Weg ( http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/ ).

Viel Glück!

4
DvS

Sie können ein einzelnes ActionResult verwenden, um mit Post und Get umzugehen:

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

Nützlich, wenn Ihre Methoden Get und Post übereinstimmende Signaturen haben.

4
DevDave

Sie können [ActionName ("NewActionName")] verwenden, um dieselbe Methode mit einem anderen Namen zu verwenden:

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

Ich brauchte eine Überladung für:

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

Es gab nur wenige Argumente, bei denen ich am Ende so vorgegangen bin:

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

Es ist keine perfekte Lösung, besonders wenn Sie viele Argumente haben, aber es funktioniert gut für mich.

2
Kasey Speakman

Ich habe dasselbe Problem auch in meiner Bewerbung. Ohne Änderung von Methodeninformationen habe ich [ActionName ("SomeMeaningfulName")] in Action head angegeben. Problem gelöst

[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
ಅನಿಲ್

Für jede Controller-Methode ist nur eine öffentliche Signatur zulässig. Wenn Sie versuchen, es zu überladen, wird es kompiliert, es wird jedoch der aufgetretene Laufzeitfehler angezeigt.

Wenn Sie nicht bereit sind, andere Verben zu verwenden (wie das [HttpGet] und [HttpPost] Attribute), um überladene Methoden zu unterscheiden (was funktionieren wird) oder das Routing zu ändern. Sie können dann entweder eine andere Methode mit einem anderen Namen angeben oder innerhalb der vorhandenen Methode versenden. So habe ich es gemacht:

Ich kam einmal in eine Situation, in der ich die Abwärtskompatibilität aufrechterhalten musste. Die ursprüngliche Methode erwartete zwei Parameter, aber der neue hatte nur einen. Überladen, wie ich es erwartet hatte, funktionierte nicht, da MVC den Einstiegspunkt nicht mehr fand.

Um das zu lösen, habe ich folgendes getan:

  1. Die 2 überladenen Aktionsmethoden wurden von öffentlich zu privat geändert
  2. Erstellt eine neue öffentliche Methode, die "nur" 2 Zeichenfolgenparameter enthält. Dieser fungierte als Disponent, d. H .:

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

Dies ist natürlich ein Hack und sollte später überarbeitet werden. Aber vorerst hat es bei mir geklappt.

Sie können auch einen Dispatcher wie folgt erstellen:

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

Sie sehen, dass UpdateAction 2 Parameter benötigt, während DeleteAction nur einen benötigt.

0
Matt

Erstellen Sie die Basismethode als virtuell

public virtual ActionResult Index()

Erstellen Sie die überschriebene Methode als Überschreibung

public override ActionResult Index()

Bearbeiten: Dies gilt offensichtlich nur, wenn sich die Überschreibungsmethode in einer abgeleiteten Klasse befindet, die anscheinend nicht die Absicht des OP war.

0
Andiih

Entschuldigung für die Verspätung. Ich war mit dem gleichen Problem konfrontiert und habe einen Link mit guten Antworten gefunden, der neuen Jungs helfen könnte

Alle Credits für die BinaryIntellect-Website und die Autoren

Grundsätzlich gibt es vier Situationen: Verwendung unterschiedlicher Verben, Verwendung von Routing, Überlastmarkierung mit [NoAction] -Attribut und = Ändere den Namen des Aktionsattributs mit [Aktionsname]

Das hängt also von Ihren Anforderungen und Ihrer Situation ab.

Wie auch immer, folgen Sie dem Link:

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

0
Eric Saboia

Ich mag diese Antwort in einem anderen Thread

Dies wird hauptsächlich verwendet, wenn Sie von einem anderen Controller erben und eine Aktion vom Basis-Controller überschreiben möchten

ASP.NET MVC - Überschreiben einer Aktion mit unterschiedlichen Parametern

0
Keyjote