Come chiamare un altro controller Azione Da un controller in Mvc


153

Ho bisogno di chiamare un'azione del controller B FileUploadMsgView dal controller A e devo passare un parametro per questo.

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

3
So che questa domanda è vecchia ma secondo me dovresti contrassegnare la risposta di Ed Chapel come la migliore, quella di Tieson sembra un trucco, è ancora valida, ma perché usare una soluzione alternativa quando puoi usarla nel modo in cui doveva essere e ottieni il risultato desiderato
Anders M.

1
@AndersM. La risposta di Ed fa un reindirizzamento. Non è quello che voglio quando ho trovato questa domanda alla ricerca di una soluzione.
mxmissile,

@mxmissile non è un coglione ma la risposta di Ed è ciò di cui ha bisogno il richiedente poiché desidera una vista che viene restituita in base a ciò che viene caricato, concordo sul fatto che il richiedente avrebbe potuto fare un lavoro migliore nel formulare la sua domanda (è questa la parola giusta? ) non possiamo saperlo, anche se il suo inglese potrebbe essere limitato, anche se la risposta di Tiesons ti ha aiutato - il che è positivo - non cambia il fatto che la risposta di Ed rifletta meglio ciò di cui ha bisogno il richiedente
Anders M.

2
@AndersM. Capisco, la mia formulazione del commento è stata semplicemente negativa ... :-) Avrei dovuto sottolineare il punto che non era il risultato che desideravo.
mxmissile,

@AndersM. Il richiedente ha accettato la risposta di Tieson come migliore, quindi non sono sicuro del motivo per cui decideresti per lui? La risposta che Tieson mi ha dato mi ha aiutato più della risposta di Ed. SO non è solo per aiutare una sola persona, ma chiunque abbia problemi simili. Quindi perché non mantenere la risposta di Tieson in cima?
Kevin Voorn,

Risposte:


106

I controller sono solo classi: ne viene creato uno nuovo e chiama il metodo di azione proprio come faresti con qualsiasi altro membro della classe:

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


76
Non ti mancheranno ControllerContext, Request e amici se lo fai solo?
cirrus,

20
L'istanza del controller non è una buona idea perché il suo ciclo di vita potrebbe essere controllato da un'altra parte dell'applicazione. Ad esempio, quando si utilizza un contenitore IoC, tutte le depdenze devono essere iniettate, ecc.
Mo Valipour,

48
Se usi IoC, puoi ottenere un controller popolato tramitevar controller = DependencyResolver.Current.GetService<ControllerB>();
mxmissile il

3
@mxmissile Vale la pena aggiungerlo come nuova risposta, piuttosto che come commento qui.
Tieson T.

2
@ilasno Hai familiarità con il termine "inversione di controllo"? Il punto che sta facendo è che se hai dei componenti nei tuoi controller che devono essere iniettati nel costruttore, la mia risposta non funziona davvero, a meno che tu non usi qualcosa come DependencyResolver come localizzatore di servizi.
Tieson T.

202

Come dice @mxmissile nei commenti alla risposta accettata, non si dovrebbe rinnovare il controller perché mancherà le dipendenze impostate per IoC e non avrà HttpContext.

Invece, dovresti ottenere un'istanza del tuo controller in questo modo:

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

Esattamente quello che stavo cercando. Si noti che coloro che non utilizzano IoC non riceveranno ancora un'iniezione HttpContext.
brichins,

var controllersarebbe assegnato il tipo ControllerB, sì.
DLeh,

1
Questo mi avvicina, ma un problema che si presenta è che nel mio caso controller.MyAction () fa riferimento a User.Identity che sembra essere privo di fondamento.
Robert H. Bourdeau,

1
@ilasno Sono arrugginito su MVC in questi giorni, ma penso di voler dire che devi effettivamente avere l' IoC impostato per ottenere un oggetto Controller completamente popolato (ad esempio un associato HttpContext). Credo di aver usato questo approccio senza alcun IoC per ottenere un oggetto controller "superficiale" (era necessario solo l'accesso a determinate funzionalità) e inizialmente ero confuso sul perché le parti fossero "mancanti". [a parte: ho lavorato su di esso mentre continuavo a utilizzare questo approccio, ma probabilmente avrei dovuto refactoring quella funzionalità su una classe condivisa.] Per quanto riguarda l'installazione e le scelte di IoC, dovrei rimandarti ad altri articoli / domande SO.
brichins,

3
Alcune persone si lasciano trasportare da inutili modifiche ... nota che qualcuno ha modificato la risposta cambiando la variabile "controller" in "ctrlr" ... quindi dovrebbe leggere "ctrlr.ControllerContext = new ControllerContext (this.Request.RequestContext, ctrl) ;" se l'utente lo ha modificato correttamente
JoeSharp il

62

Il tuo campione sembra un codice psuedo. Devi restituire il risultato di RedirectToAction:

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

4
Va sottolineato che se l'azione target accetta solo POST, ciò non funzionerà.
Marco Alves,

13
Ciò restituisce un 302 che provoca un altro hit sul server che non è quello che pone la domanda.
rboarman,

16

come dice @DLeh Usa piuttosto

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

Tuttavia, fornendo al controller, un contesto di controller è importante soprattutto quando è necessario accedere Userall'oggetto, Serverall'oggetto o HttpContextall'interno del controller "figlio".

Ho aggiunto una riga di codice:

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

altrimenti avresti potuto usare System.Web anche per accedere al contesto attuale, per accedere Serveragli oggetti citati in precedenza

NB: sto prendendo di mira il framework versione 4.6 (Mvc5)


4
Se si tenta di richiamare un'azione nel controller che utilizza View (..) o PartialView (...) è necessario modificare manualmente routeData, in modo che ASP.NET sappia come trovare la vista. controller.RouteData.Values["controller"] = "Home";controller.RouteData.Values["action"] = "Index";Supponendo che si stia tentando di restituire il risultato dall'azione Index in HomeController.
Steven,

@Steven ho dovuto applicare questi valori thispiuttosto che controller. Alla fine il risultato ritorna attraverso il controller locale (questo), quindi è quello che finisce per cercare la vista.
aaaantoine,

Aggiungerei anche che la proprietà Url non è inizializzata su DependencyResolver.Current.GetService <ControllerB> (). Quindi devi copiarlo manualmente dal controller corrente.
Ralfeus,

return View("ViewName");return View();
Nell'azione

9

Lascia che il resolver lo faccia automaticamente.

All'interno di un controller:

public class AController : ApiController
{
    private readonly BController _bController;

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

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

}

2
imo la risposta più pulita, ma è necessario impostare il contesto del controller sul nuovo controller.
Mafii,

8

Se qualcuno sta osservando come farlo nel core .net, l'ho realizzato aggiungendo il controller all'avvio

services.AddTransient<MyControllerIwantToInject>();

Quindi iniettarlo nell'altro controller

public class controllerBeingInjectedInto : ControllerBase
{
    private readonly MyControllerIwantToInject _myControllerIwantToInject

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

Quindi chiamalo così _myControllerIwantToInject.MyMethodINeed();


4

Questo è esattamente quello che stavo cercando dopo aver scoperto che RedirectToAction()non avrebbe superato oggetti di classe complessi.

Ad esempio, desidero chiamare il IndexComparisonmetodo nel LifeCycleEffectsResultscontroller e passargli un oggetto classe complesso chiamato modello.

Ecco il codice non riuscito:

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

Vale la pena notare che stringhe, numeri interi, ecc. Stavano sopravvivendo al viaggio verso questo metodo del controller, ma gli oggetti di elenco generici soffrivano di ciò che ricordava le perdite di memoria C.

Come raccomandato sopra, ecco il codice che ho sostituito con:

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

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

Ora tutto funziona come previsto. Grazie per aver aperto la strada.


3

La risposta di Dleh è corretta e spiega come ottenere un'istanza di un altro controller senza le dipendenze mancanti impostate per IoC

Tuttavia, ora dobbiamo chiamare il metodo da questo altro controller.
La risposta completa sarebbe:

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

//Call your method
ActionInvoker.InvokeAction(controller.ControllerContext, "MethodNameFromControllerB_ToCall");

Come si chiama l'azione "MethodNameFromControllerB_ToCall" se prevede parametri? per esempio MethodNameFromControllerB_ToCall (int somenum, string sometext)?
Patee Gutee,

3

So che è vecchio, ma puoi:

  • Crea un livello di servizio
  • Sposta metodo lì
  • Metodo di chiamata in entrambi i controller

2

se il problema è chiamare. puoi chiamarlo usando questo metodo.

yourController obj= new yourController();

obj.yourAction();

1
Pfft! E se invece ti aspetti un risultato da un'azione? var res = new ControllerB().SetUpTimer(new TimeSpan(23, 20, 00));
DirtyBit,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.