So rufen Sie einen anderen Controller auf Aktion Von einem Controller in Mvc

153

Ich muss eine Controller B-Aktion FileUploadMsgView von Controller A aus aufrufen und einen Parameter dafür übergeben.

 Code---its not going to the controller B's FileUploadMsgView().
    In ControllerA
  private void Test()
    {

        try
        {//some codes here
            ViewBag.FileUploadMsg = "File uploaded successfully.";
            ViewBag.FileUploadFlag = "2";

            RedirectToAction("B", "FileUploadMsgView", new { FileUploadMsg = "File   uploaded successfully" });
        }

     In ControllerB receiving part
  public ActionResult FileUploadMsgView(string FileUploadMsg)
    {
         return View();
    }
user2156088
quelle
3
Ich weiß, dass diese Frage alt ist, aber meiner Meinung nach sollten Sie die Antwort von ed chapel als die beste markieren. Krawatten sehen aus wie ein Hack, sie sind immer noch gültig, aber warum sollten Sie eine Problemumgehung verwenden, wenn Sie sie so verwenden können, wie sie sein sollte? und erhalten Sie das gewünschte Ergebnis
Anders M.
1
@AndersM. Eds Antwort führt eine Weiterleitung durch. Das ist nicht das, was ich will, als ich diese Frage auf der Suche nach einer Lösung fand.
mxmissile
@mxmissile, um kein Schwanz zu sein, aber Eds Antwort ist das, was der Fragesteller braucht, da er eine Ansicht möchte, die basierend auf dem, was hochgeladen wird, zurückgegeben wird. Ich stimme zu, dass der Fragesteller seine Frage besser hätte formulieren können (ist dies das richtige Wort? ) Wir können dies jedoch nicht wissen, da sein Englisch möglicherweise eingeschränkt ist, obwohl Tiesons Antwort Ihnen geholfen hat - was gut ist - es ändert nichts an der Tatsache, dass Eds Antwort am besten widerspiegelt, was der Fragesteller braucht
Anders M.
2
@AndersM. Ich verstehe, mein Kommentar Formulierung war einfach nur schlecht ... :-) ich den Punkt betont haben sollte , die nicht das Ergebnis war ich gewünscht.
mxmissile
@AndersM. Der Fragesteller akzeptierte die Antwort von Tieson als am besten, also bin ich mir nicht sicher, warum Sie sich für ihn entscheiden würden? Die Antwort, die Tieson mir gab, half mir mehr als die Antwort, die Ed antwortete. SO ist nicht nur dazu da, einer einzelnen Person zu helfen, sondern jedem, der ähnliche Probleme hat. Warum also nicht einfach Tiesons Antwort im Auge behalten?
Kevin Voorn

Antworten:

106

Controller sind nur Klassen - neu und rufen die Aktionsmethode wie jedes andere Klassenmitglied auf:

var result = new ControllerB().FileUploadMsgView("some string");

Tieson T.
quelle
76
Vermissen Sie nicht ControllerContext, Request und Freunde, wenn Sie dies einfach tun?
Cirrus
20
Die Instanziierung des Controllers ist keine gute Idee, da der Lebenszyklus möglicherweise von einem anderen Teil der Anwendung gesteuert wird. ZB bei Verwendung eines IoC-Behälters sollten alle Abhängigkeiten injiziert werden usw.
Mo Valipour
48
Wenn Sie IoC verwenden, können Sie einen bestückten Controller übervar controller = DependencyResolver.Current.GetService<ControllerB>();
mxmissile
3
@mxmissile Das ist es wert, als neue Antwort hinzugefügt zu werden, anstatt hier einen Kommentar abzugeben.
Tieson T.
2
@ilasno Kennen Sie den Begriff "Umkehrung der Kontrolle"? Der Punkt, den er macht, ist, dass wenn Sie Komponenten in Ihren Controllern haben, die in den Konstruktor eingefügt werden müssen, meine Antwort nicht wirklich funktioniert, es sei denn, Sie verwenden so etwas wie den DependencyResolver als Service-Locator.
Tieson T.
202

Wie @mxmissile in den Kommentaren zur akzeptierten Antwort sagt, sollten Sie den Controller nicht neu einrichten, da ihm die für IoC eingerichteten Abhängigkeiten fehlen und die nicht vorhanden sind HttpContext.

Stattdessen sollten Sie eine Instanz Ihres Controllers wie folgt erhalten:

var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);
DLeh
quelle
Genau das, wonach ich gesucht habe. Beachten Sie, dass diejenigen, die IoC nicht verwenden, immer noch keine HttpContextInjektion erhalten.
Brichins
var controllerwürde der Typ zugewiesen werden ControllerB, ja.
DLeh
1
Dies bringt mich näher, aber ein Problem, das auftritt, ist, dass in meinem Fall controller.MyAction () auf User.Identity verweist, die unbegründet zu sein scheint.
Robert H. Bourdeau
1
@ilasno Ich bin rostig in diesen Tagen auf MVC, aber ich denke , ich meinte , dass Sie tatsächlich haben IoC Einrichtung eines voll bestückten Controller - Objekt zu erhalten (zB ein zugehöriges HttpContext). Ich glaube, ich habe diesen Ansatz ohne IoC verwendet, um ein "flaches" Controller-Objekt zu erhalten (brauchte nur Zugriff auf bestimmte Funktionen) und war anfangs verwirrt darüber, warum Teile "fehlten". [beiseite: Ich habe es umgangen, während ich diesen Ansatz noch verwendet habe, aber wahrscheinlich hätte ich diese Funktionalität in eine gemeinsam genutzte Klasse umgestalten sollen.] Was das IoC-Setup und die Auswahl betrifft, müsste ich Sie auf andere Artikel / SO-Fragen verweisen.
Brichins
3
Einige Leute werden von sinnlosen Änderungen mitgerissen ... beachten Sie, dass jemand die Antwort bearbeitet hat und die Variable "controller" in "ctrlr" geändert hat ... daher sollte sie "ctrlr.ControllerContext = new ControllerContext (this.Request.RequestContext, ctrl) lauten. ; " Wenn dieser Benutzer es richtig bearbeitet hat
JoeSharp
62

Ihr Beispiel sieht aus wie Pseudocode. Sie müssen zurückkehren das Ergebnis RedirectToAction:

return RedirectToAction("B", 
                        "FileUploadMsgView",
                        new { FileUploadMsg = "File uploaded successfully" });
Ed Chapel
quelle
4
Es muss darauf hingewiesen werden, dass dies nicht funktioniert, wenn die Zielaktion nur POST akzeptiert.
Marco Alves
13
Dies gibt einen 302 zurück, der einen weiteren Treffer auf dem Server verursacht, was nicht die Frage ist.
Rboarman
16

wie @DLeh sagt Verwenden Sie lieber

var controller = DependencyResolver.Current.GetService<ControllerB>();

Wenn Sie dem Controller jedoch einen Controller-Kontext geben, ist dies besonders wichtig, wenn Sie auf das UserObjekt, das ServerObjekt oder das zugreifen müssenHttpContext Innere des untergeordneten Controllers .

Ich habe eine Codezeile hinzugefügt:

controller.ControllerContext = new ControllerContext(Request.RequestContext, controller);

Oder Sie hätten System.Web verwenden können, um auch auf den aktuellen Kontext zuzugreifen und darauf zuzugreifen Server zuzugreifen, auf Objekte oder die frühen Metased-Objekte zuzugreifen

NB: Ich ziele auf das Framework Version 4.6 (Mvc5)

Nishanth Shaan
quelle
4
Wenn Sie versuchen, eine Aktion im Controller aufzurufen, die View (..) oder PartialView (...) verwendet, müssen Sie die routeData manuell ändern, damit ASP.NET weiß, wie Sie Ihre Ansicht finden. controller.RouteData.Values["controller"] = "Home";controller.RouteData.Values["action"] = "Index";Angenommen, Sie versuchen, das Ergebnis der Indexaktion in HomeController zurückzugeben.
Steven
@Steven Ich musste diese Werte thiseher anwenden als auf controller. Letztendlich kommt das Ergebnis über den lokalen Controller (dies) zurück, sodass am Ende versucht wird, die Ansicht zu finden.
aaaantoine
Ich möchte auch hinzufügen, dass die Url-Eigenschaft bei DependencyResolver.Current.GetService <ControllerB> () nicht initialisiert wird. Sie müssen es also manuell vom aktuellen Controller kopieren.
Ralfeus
In der Targeting-Aktion sollten Sie return View("ViewName");stattdessen nurreturn View();
mNejkO
9

Lassen Sie den Resolver das automatisch tun.

Innerhalb eines Controllers:

public class AController : ApiController
{
    private readonly BController _bController;

    public AController(
    BController bController)
    {
        _bController = bController;
    }

    public httpMethod{
    var result =  _bController.OtherMethodBController(parameters);
    ....
    }

}
David Castro
quelle
2
Ich bin die sauberste Antwort, aber Sie sollten den Controller-Kontext auf den neuen Controller einstellen.
Mafii
8

Wenn jemand darüber nachdenkt, wie dies im .net-Kern gemacht wird, habe ich dies erreicht, indem ich den Controller beim Start hinzugefügt habe

services.AddTransient<MyControllerIwantToInject>();

Dann Injizieren in den anderen Controller

public class controllerBeingInjectedInto : ControllerBase
{
    private readonly MyControllerIwantToInject _myControllerIwantToInject

     public controllerBeingInjectedInto(MyControllerIwantToInject myControllerIwantToInject)
{
       _myControllerIwantToInject = myControllerIwantToInject;
      }

Dann nenne es einfach so _myControllerIwantToInject.MyMethodINeed();

Leonardo Wildt
quelle
4

Genau das habe ich gesucht, nachdem ich das gefunden hatte RedirectToAction() komplexe Klassenobjekte nicht übergeben werden.

Als Beispiel möchte ich die IndexComparisonMethode in der aufrufenLifeCycleEffectsResults Steuerung und ihr ein komplexes Klassenobjekt mit dem Namen model übergeben.

Hier ist der Code, der fehlgeschlagen ist:

return RedirectToAction("IndexComparison", "LifeCycleEffectsResults", model);

Bemerkenswert ist, dass Strings, Ganzzahlen usw. die Reise zu dieser Controller-Methode überlebten, aber generische Listenobjekte unter dem litten, was an C-Speicherlecks erinnerte.

Wie oben empfohlen, ist hier der Code, durch den ich ihn ersetzt habe:

var controller = DependencyResolver.Current.GetService<LifeCycleEffectsResultsController>();

var result = controller.IndexComparison(model);
return result;

Alles funktioniert jetzt wie vorgesehen. Vielen Dank, dass Sie den Weg weisen.

cghore
quelle
3

Dlehs Antwort ist richtig und erklärt, wie eine Instanz eines anderen Controllers abgerufen werden kann, ohne dass für IoC eingerichtete Abhängigkeiten fehlen

Jetzt müssen wir die Methode jedoch von diesem anderen Controller aus aufrufen.
Die vollständige Antwort wäre:

var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);

//Call your method
ActionInvoker.InvokeAction(controller.ControllerContext, "MethodNameFromControllerB_ToCall");
AlexB
quelle
Wie ruft man die Aktion "MethodNameFromControllerB_ToCall" auf, wenn sie Parameter erwartet? Zum Beispiel MethodNameFromControllerB_ToCall (int somenum, string sometext)?
Patee Gutee
3

Ich weiß, dass es alt ist, aber Sie können:

  • Erstellen Sie eine Service-Schicht
  • Methode dorthin verschieben
  • Aufrufmethode in beiden Controllern
Watth
quelle
2

wenn das Problem ist anzurufen. Sie können es mit dieser Methode aufrufen.

yourController obj= new yourController();

obj.yourAction();

quelle
1
Pfft! Was ist, wenn Sie stattdessen ein Ergebnis von einer Aktion erwarten? var res = new ControllerB().SetUpTimer(new TimeSpan(23, 20, 00));
DirtyBit