Cómo llamar a otro controlador Acción desde un controlador en Mvc

153

Necesito llamar a una acción del controlador B FileUploadMsgView desde el controlador A y necesito pasarle un parámetro.

 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();
    }
usuario2156088
fuente
3
Sé que esta pregunta es antigua, pero en mi opinión, debe marcar la respuesta de ed chapel como la mejor, tieons parece un truco, todavía es válido, pero ¿por qué usar una solución alternativa cuando puede usarla de la manera en que debe ser? y obtenga el resultado deseado
Anders M.
1
@AndersM. La respuesta de Ed hace una redirección. Eso no es lo que quiero cuando encontré esta pregunta buscando una solución.
mxmissile
@mxmissile no es un imbécil, pero la respuesta de Ed es lo que necesita el autor de la pregunta, ya que quiere una vista que se devuelva en función de lo que se carga, estoy de acuerdo en que el autor de la pregunta podría haber hecho un mejor trabajo al formular su pregunta (¿es esta la palabra correcta? ) no podemos saber esto, ya que su inglés puede ser limitado, a pesar de que la respuesta de Tiesons lo ayudó, lo cual es bueno, no cambia el hecho de que la respuesta de Ed refleja mejor lo que necesita el autor de la pregunta
Anders M.
2
@AndersM. Entiendo, mi redacción de comentarios fue simplemente mala ... :-) Debería haber enfatizado el punto que no era el resultado que deseaba.
mxmissile
@AndersM. El autor de la pregunta aceptó la respuesta de Tieson como la mejor, así que no estoy seguro de por qué decidirías por él. La respuesta que me dio Tieson me ayudó más que la respuesta de Ed. SO no es solo para ayudar a una sola persona, sino a todos los que tienen problemas similares. Entonces, ¿por qué no mantener la respuesta de Tieson en la cima?
Kevin Voorn

Respuestas:

106

Los controladores son solo clases: uno nuevo y llama al método de acción como lo haría con cualquier otro miembro de la clase:

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

Tieson T.
fuente
76
¿No te perderás ControllerContext, Request y amigos si solo haces esto?
cirrus
20
La creación de instancias del controlador no es una buena idea porque su ciclo de vida podría ser controlado por otra parte de la aplicación. Por ejemplo cuando se utiliza un contenedor IoC deben inyectarse todas depdencies, etc.
Mo Valipour
48
Si usa IoC, puede obtener un controlador poblado a través devar controller = DependencyResolver.Current.GetService<ControllerB>();
mxmissile
3
@mxmissile Vale la pena agregarlo como una nueva respuesta, en lugar de un comentario aquí.
Tieson T.
2
@ilasno ¿Está familiarizado con el término "inversión de control"? El punto que está señalando es que si tiene componentes en sus controladores que necesitan ser inyectados en el constructor, mi respuesta realmente no funciona, a menos que use algo como DependencyResolver como un localizador de servicios.
Tieson T.
202

Como @mxmissile dice en los comentarios a la respuesta aceptada, no debe renovar el controlador porque le faltarán dependencias configuradas para IoC y no tendrá el HttpContext.

En cambio, debería obtener una instancia de su controlador como esta:

var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);
DLeh
fuente
Exactamente lo que estaba buscando. Tenga en cuenta que aquellos que no usan IoC aún no recibirán una HttpContextinyección.
brichins
var controllerse le asignaría el tipo ControllerB, sí.
DLeh
1
Esto me acerca, pero un problema que surge es que, en mi caso, controller.MyAction () hace referencia a User.Identity, que parece no estar implementado.
Robert H. Bourdeau
1
@ilasno Estoy oxidado en MVC en estos días, pero creo que quise decir que realmente tienes que configurar IoC para obtener un objeto Controller completamente poblado (por ejemplo, un asociado HttpContext). Creo que usé este enfoque sin ningún IoC para obtener un objeto controlador "superficial" (solo necesitaba acceso a cierta funcionalidad) y al principio estaba confundido acerca de por qué faltaban las partes. [aparte: trabajé alrededor mientras todavía usaba este enfoque, pero probablemente debería haber reestructurado esa funcionalidad en una clase compartida.] En cuanto a la configuración y las opciones de IoC, tendría que remitirlo a otros artículos / preguntas SO.
brichins
3
Algunas personas se dejan llevar por ediciones sin sentido ... tenga en cuenta que alguien editó la respuesta cambiando la variable "controlador" a "ctrlr" ... por lo que debería leer "ctrlr.ControllerContext = new ControllerContext (this.Request.RequestContext, ctrl) ; " si ese usuario lo editó correctamente
JoeSharp
62

Su muestra se parece al código psuedo. Debe devolver el resultado de RedirectToAction:

return RedirectToAction("B", 
                        "FileUploadMsgView",
                        new { FileUploadMsg = "File uploaded successfully" });
Capilla Ed
fuente
44
Debe señalarse que si la acción objetivo solo acepta POST, esto no funcionará.
Marco Alves
13
Esto devuelve un 302 que causa otro golpe al servidor que no es lo que hace la pregunta.
rboarman
16

como @DLeh dice Usar más bien

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

Pero, al darle al controlador, un contexto de controlador es importante, especialmente cuando necesita acceder al Userobjeto, Serverobjeto o al HttpContextinterior del controlador 'secundario'.

He agregado una línea de código:

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

o de lo contrario podría haber utilizado System.Web para acceder también al contexto actual, para acceder a Serverlos objetos mencionados anteriormente

NB: estoy apuntando al framework versión 4.6 (Mvc5)

Nishanth Shaan
fuente
44
Si intenta llamar a una acción en el controlador que usa View (..) o PartialView (...), debe cambiar manualmente routeData, para que ASP.NET sepa cómo encontrar su vista. controller.RouteData.Values["controller"] = "Home";controller.RouteData.Values["action"] = "Index";Suponiendo que está intentando devolver el resultado de la acción de Índice en HomeController.
Steven
@ Steven tuve que aplicar estos valores en thislugar de hacerlo controller. Finalmente, el resultado regresa a través del controlador local (esto), así que eso es lo que termina tratando de encontrar la vista.
aaaantoine
También agregaría que la propiedad Url no se inicializa en DependencyResolver.Current.GetService <ControllerB> (). Por lo tanto, debe copiarlo desde el controlador actual manualmente.
Ralfeus
En la Acción de focalización, deberías usar return View("ViewName");soloreturn View();
mNejkO
9

Deje que el resolutor lo haga automáticamente.

Dentro de un controlador:

public class AController : ApiController
{
    private readonly BController _bController;

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

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

}
David Castro
fuente
2
Es la respuesta más clara, pero debe establecer el contexto del controlador en el nuevo controlador.
Mafii
8

Si alguien está buscando cómo hacer esto en .net core, lo logré agregando el controlador en el inicio

services.AddTransient<MyControllerIwantToInject>();

Luego inyectándolo en el otro controlador

public class controllerBeingInjectedInto : ControllerBase
{
    private readonly MyControllerIwantToInject _myControllerIwantToInject

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

Entonces solo llámalo así _myControllerIwantToInject.MyMethodINeed();

Leonardo Wildt
fuente
4

Esto es exactamente lo que estaba buscando después de encontrar eso RedirectToAction() no pasaría objetos de clase complejos.

Como ejemplo, quiero llamar al IndexComparisonmétodo en elLifeCycleEffectsResults controlador y pasarle un objeto de clase complejo llamado modelo.

Aquí está el código que falló:

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

Vale la pena señalar que las cadenas, los enteros, etc., sobrevivieron al viaje a este método de controlador, pero los objetos genéricos de la lista estaban sufriendo lo que recordaba las pérdidas de memoria C.

Como se recomienda anteriormente, aquí está el código con el que lo reemplacé:

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

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

Todo está funcionando según lo previsto ahora. Gracias por liderar el camino.

cghore
fuente
3

La respuesta de Dleh es correcta y explica cómo obtener una instancia de otro controlador sin perder dependencias configuradas para IoC

Sin embargo, ahora necesitamos llamar al método desde este otro controlador.
La respuesta completa sería:

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

//Call your method
ActionInvoker.InvokeAction(controller.ControllerContext, "MethodNameFromControllerB_ToCall");
AlexB
fuente
¿Cómo se llama a la acción "MethodNameFromControllerB_ToCall" si espera parámetros? por ejemplo MethodNameFromControllerB_ToCall (int somenum, string sometext)?
Patee Gutee
3

Sé que es viejo, pero puedes:

  • Crear una capa de servicio
  • Mover método allí
  • Método de llamada en ambos controladores
Vatio
fuente
2

Si el problema es llamar. Puedes llamarlo usando este método.

yourController obj= new yourController();

obj.yourAction();

fuente
1
¡No! ¿Qué sucede si espera un resultado de una acción? var res = new ControllerB().SetUpTimer(new TimeSpan(23, 20, 00));
DirtyBit