Comment appeler un autre contrôleur Action à partir d'un contrôleur dans Mvc

153

Je dois appeler une action du contrôleur B FileUploadMsgView à partir du contrôleur A et je dois lui transmettre un paramètre.

 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
la source
3
Je sais que cette question est ancienne, mais à mon avis, vous devriez marquer la réponse de la chapelle ed comme la meilleure, Tieson ressemble à un hack, elle est toujours valide, mais pourquoi utiliser une solution de contournement lorsque vous pouvez l'utiliser comme elle était censée être et obtenez le résultat souhaité
Anders M.
1
@AndersM. La réponse d'Ed fait une redirection. Ce n'est pas ce que je veux quand j'ai trouvé cette question à la recherche d'une solution.
mxmissile
@mxmissile pour ne pas être une bite mais la réponse d'Ed est ce dont le demandeur a besoin puisqu'il veut une vue renvoyée en fonction de ce qui est téléchargé, je conviens que le demandeur aurait pu faire un meilleur travail pour formuler sa question (est-ce le bon mot? ), nous ne pouvons pas le savoir car son anglais peut être limité, même si la réponse de Tiesons vous a aidé - ce qui est bien - cela ne change pas le fait que la réponse d'Ed reflète le mieux ce dont le demandeur a besoin
Anders M.
2
@AndersM. Je comprends, la formulation de mon commentaire était tout simplement mauvaise ... :-) J'aurais dû souligner le point qui n'était pas le résultat que je souhaitais.
mxmissile
@AndersM. Le demandeur a accepté la réponse de Tieson comme étant la meilleure, donc je ne sais pas pourquoi vous décideriez pour lui? La réponse que Tieson m'a donnée m'a aidé plus que la réponse d'Ed. SO ne sert pas seulement à aider une seule personne, mais à tous ceux qui ont des problèmes similaires. Alors pourquoi ne pas simplement garder la réponse de Tieson en tête?
Kevin Voorn

Réponses:

106

Les contrôleurs ne sont que des classes - un nouveau et appelez la méthode d'action comme vous le feriez pour n'importe quel autre membre de la classe:

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

Tieson T.
la source
76
Ne manquerez-vous pas ControllerContext, Request et amis si vous faites juste cela?
cirrus
20
L'instanciation du contrôleur n'est pas une bonne idée car son cycle de vie peut être contrôlé par une autre partie de l'application. Par exemple, lors de l'utilisation d'un conteneur IoC, toutes les dépendances doivent être injectées, etc.
Mo Valipour
48
Si vous utilisez IoC, vous pouvez obtenir un contrôleur var controller = DependencyResolver.Current.GetService<ControllerB>();
rempli
3
@mxmissile Cela vaut la peine d'être ajouté comme nouvelle réponse, plutôt que comme commentaire ici.
Tieson T.Décembre
2
@ilasno Connaissez-vous le terme «inversion de contrôle»? Le point qu'il fait valoir est que si vous avez des composants dans vos contrôleurs qui doivent être injectés dans le constructeur, ma réponse ne fonctionne pas vraiment, à moins que vous n'utilisiez quelque chose comme DependencyResolver comme localisateur de service.
Tieson T.décembre
202

Comme @mxmissile le dit dans les commentaires sur la réponse acceptée, vous ne devriez pas mettre à jour le contrôleur car il manquera les dépendances configurées pour IoC et n'aura pas l'extension HttpContext.

Au lieu de cela, vous devriez obtenir une instance de votre contrôleur comme ceci:

var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);
DLeh
la source
Exactement ce que je cherchais. Notez que ceux qui n'utilisent pas IoC ne seront toujours pas HttpContextinjectés.
brichins
var controllerserait attribué le type ControllerB, oui.
DLeh
1
Cela me rapproche, mais un problème qui se pose est que dans mon cas, controller.MyAction () fait référence à User.Identity qui semble être institué.
Robert H.Bourdeau
1
@ilasno Je suis rouillé sur MVC ces jours -ci , mais je pense que je voulais dire que vous devez réellement avoir IoC mis en place pour obtenir un objet contrôleur entièrement rempli (par exemple un associé HttpContext). Je crois que j'ai utilisé cette approche sans aucun IoC pour obtenir un objet contrôleur "superficiel" (juste besoin d'accéder à certaines fonctionnalités) et j'étais initialement confus quant à la raison pour laquelle des pièces étaient "manquantes". [À part: j'ai contourné le problème tout en utilisant cette approche, mais j'aurais probablement dû refactoriser cette fonctionnalité dans une classe partagée.] En ce qui concerne la configuration et les choix d'IoC, je devrais vous référer à d'autres articles / questions SO.
brichins
3
Certaines personnes s'emballent avec des modifications inutiles ... notez que quelqu'un a modifié la réponse en changeant la variable "controller" en "ctrlr" ... donc il devrait lire "ctrlr.ControllerContext = new ControllerContext (this.Request.RequestContext, ctrl) ; " si cet utilisateur l'a modifié correctement
JoeSharp
62

Votre exemple ressemble à du code psuedo. Vous devez renvoyer le résultat de RedirectToAction:

return RedirectToAction("B", 
                        "FileUploadMsgView",
                        new { FileUploadMsg = "File uploaded successfully" });
Chapelle Ed
la source
4
Il faut souligner que si l'action cible n'accepte que POST, cela ne fonctionnera pas.
Marco Alves
13
Cela renvoie un 302 qui provoque un autre hit sur le serveur, ce qui n'est pas ce que la question demande.
rboarman
16

comme @DLeh dit Utiliser plutôt

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

Mais, en donnant au contrôleur, un contexte de contrôleur est important, en particulier lorsque vous devez accéder à l' Userobjet, à l' Serverobjet ou à l' HttpContextintérieur du contrôleur «enfant».

J'ai ajouté une ligne de code:

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

ou bien vous auriez pu utiliser System.Web pour accéder également au contexte actuel, pour accéder Serveraux objets mentionnés plus tôt

NB: je cible le framework version 4.6 (Mvc5)

Nishanth Shaan
la source
4
Si vous essayez d'appeler une action dans le contrôleur qui utilise View (..) ou PartialView (...), vous devez modifier manuellement la routeData, afin qu'ASP.NET sache comment trouver votre vue. controller.RouteData.Values["controller"] = "Home";controller.RouteData.Values["action"] = "Index";En supposant que vous essayez de renvoyer le résultat de l'action Index dans HomeController.
Steven
@Steven j'ai dû appliquer ces valeurs à thisplutôt qu'à controller. En fin de compte, le résultat revient via le contrôleur local (this), c'est donc ce qui finit par essayer de trouver la vue.
aaaantoine
J'ajouterais également que la propriété Url n'est pas initialisée sur DependencyResolver.Current.GetService <ControllerB> (). Vous devez donc le copier manuellement à partir du contrôleur actuel.
Ralfeus
Dans l'action de ciblage, vous devez utiliser à la return View("ViewName");place simplementreturn View();
mNejkO
9

Laissez le résolveur faire cela automatiquement.

À l'intérieur d'un contrôleur:

public class AController : ApiController
{
    private readonly BController _bController;

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

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

}
David Castro
la source
2
imo la réponse la plus claire, mais vous devez définir le contexte du contrôleur sur le nouveau contrôleur.
Mafii
8

Si quelqu'un cherche comment faire cela dans .net core, je l'ai accompli en ajoutant le contrôleur au démarrage

services.AddTransient<MyControllerIwantToInject>();

Puis l'injecter dans l'autre contrôleur

public class controllerBeingInjectedInto : ControllerBase
{
    private readonly MyControllerIwantToInject _myControllerIwantToInject

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

Alors appelle ça comme ça _myControllerIwantToInject.MyMethodINeed();

Leonardo Wildt
la source
4

C'est exactement ce que je recherchais après avoir découvert que RedirectToAction()les objets de classe complexes ne passeraient pas.

Par exemple, je veux appeler la IndexComparisonméthode dans le LifeCycleEffectsResultscontrôleur et lui passer un objet de classe complexe nommé model.

Voici le code qui a échoué:

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

Il convient de noter que les chaînes, les entiers, etc. survivaient au voyage vers cette méthode de contrôleur, mais les objets de liste génériques souffraient de ce qui rappelait les fuites de mémoire C.

Comme recommandé ci-dessus, voici le code que je l'ai remplacé par:

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

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

Tout fonctionne comme prévu maintenant. Merci d'avoir montré la voie.

cghore
la source
3

La réponse de Dleh est correcte et explique comment obtenir une instance d'un autre contrôleur sans dépendances manquantes configurées pour IoC

Cependant, nous devons maintenant appeler la méthode depuis cet autre contrôleur.
La réponse complète serait:

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

//Call your method
ActionInvoker.InvokeAction(controller.ControllerContext, "MethodNameFromControllerB_ToCall");
AlexB
la source
Comment appeler l'action "MethodNameFromControllerB_ToCall" si elle attend des paramètres? par exemple MethodNameFromControllerB_ToCall (int somenum, string someext)?
Patee Gutee
3

Je sais que c'est vieux, mais vous pouvez:

  • Créer une couche de service
  • Déplacer la méthode là-bas
  • Méthode d'appel dans les deux contrôleurs
Watth
la source
2

si le problème est d'appeler. vous pouvez l'appeler en utilisant cette méthode.

yourController obj= new yourController();

obj.yourAction();

la source
1
Pfft! Et si vous attendez plutôt un résultat d'une action? var res = new ControllerB().SetUpTimer(new TimeSpan(23, 20, 00));
DirtyBit