Como chamar outro controlador Ação De um controlador no Mvc


153

Preciso chamar uma ação do controlador B FileUploadMsgView do Controlador A e preciso passar um parâmetro para ele.

 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
Eu sei que essa pergunta é antiga, mas, na minha opinião, você deve marcar a resposta da ed capela como a melhor, o tieson parece um hack, ainda é válido, mas por que usar uma solução alternativa quando você pode usá-la da maneira que deveria ser? e obtenha o resultado desejado
Anders M.

1
@AndersM. A resposta de Ed faz um redirecionamento. Não é isso que eu quero quando encontrei essa pergunta em busca de uma solução.
Mxmissile

@mxmissile para não ser um idiota, mas a resposta de Ed é o que o solicitante precisa, pois ele quer uma opinião que seja retornada com base no que é enviado, concordo que o solicitante poderia ter feito um trabalho melhor ao formular sua pergunta (essa é a palavra certa? ) não podemos saber disso, pois o inglês dele pode ser limitado, embora a resposta de Tiesons tenha ajudado você - o que é bom - não muda o fato de que a resposta de Ed reflete melhor o que o solicitante precisa
Anders M.

2
@AndersM. Eu entendo, meu comentário foi ruim ... :-) Eu deveria ter enfatizado o ponto que não era o resultado que eu desejava.
Mxmissile

@AndersM. O autor da pergunta aceitou a resposta de Tieson como a melhor, então não sei por que você decidiria por ele? A resposta que Tieson me deu me ajudou mais do que a resposta de Ed. O SO não serve apenas para ajudar uma pessoa, mas para todos que têm problemas semelhantes. Então, por que não manter a resposta de Tieson no topo?
precisa

Respostas:


106

Os controladores são apenas classes - são novos e chamam o método de ação como você faria com qualquer outro membro da classe:

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


76
Você não sentirá falta do ControllerContext, Request e amigos se fizer isso?
quer

20
A instanciação do controlador não é uma boa ideia, pois seu ciclo de vida pode ser controlado por outra parte do aplicativo. Por exemplo, quando se utiliza um recipiente COI todos depdencies deve ser injectado, etc
Mo Valipour

48
Se o seu usando IoC, você pode obter um controlador povoada viavar controller = DependencyResolver.Current.GetService<ControllerB>();
mxmissile

3
@mxmissile Vale a pena adicionar como uma nova resposta, em vez de comentar aqui.
Tieson T.

2
@ilasno Você conhece o termo "inversão de controle"? O que ele está argumentando é que, se você tem componentes em seus controladores que precisam ser injetados no construtor, minha resposta não funciona realmente, a menos que você use algo como o DependencyResolver como localizador de serviço.
Tieson T.

202

Como @mxmissile diz nos comentários para a resposta aceita, você não deve atualizar o controlador porque faltam dependências configuradas para IoC e não terão o HttpContext.

Em vez disso, você deve obter uma instância do seu controlador como esta:

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

Exatamente o que eu estava procurando. Observe que aqueles que não usam IoC ainda não serão HttpContextinjetados.
Brichins 28/08/2015

var controllerseria atribuído o tipo ControllerB, sim.
DLeh

1
Isso me aproxima, mas um problema que surge é que, no meu caso, controller.MyAction () faz referência a User.Identity, que parece ter sido instanciada.
Robert H. Bourdeau

1
@ilasno estou enferrujado em MVC nos dias de hoje, mas eu acho que significa que você tem que realmente tem IoC configurado para obter um objeto controlador totalmente preenchida (por exemplo, um associado HttpContext). Acredito que usei essa abordagem sem qualquer IoC para obter um objeto de controlador "superficial" (apenas precisava de acesso a determinadas funcionalidades) e fiquei inicialmente confuso sobre o motivo pelo qual as peças estavam "ausentes". [aparte: eu trabalhei com isso enquanto ainda estava usando essa abordagem, mas provavelmente deveria ter refatorado essa funcionalidade para uma classe compartilhada.] Quanto à configuração e às opções de IoC, eu teria que encaminhá-lo para outros artigos / perguntas sobre SO.
Brichins

3
Algumas pessoas se empolgam com edições inúteis ... observe que alguém editou a resposta alterando a variável "controller" para "ctrlr" ... então deve ler "ctrlr.ControllerContext = new ControllerContext (this.Request.RequestContext, ctrl) ; " se o usuário editado-lo corretamente
JoeSharp

62

Sua amostra se parece com o código psuedo. Você precisa retornar o resultado de RedirectToAction:

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

4
Deve-se ressaltar que, se a ação de destino aceitar apenas o POST, isso não funcionará.
Marco Alves

13
Isso retorna um 302 que causa outro acerto no servidor, o que não é o que a pergunta faz.
Rboarman

16

como @DLeh diz Use

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

Mas, fornecendo ao controlador, um contexto controlller é importante, especialmente quando você precisa acessar o Userobjeto, Serverobjeto ou o HttpContextinterior do controlador 'filho'.

Eu adicionei uma linha de código:

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

ou então você poderia ter usado o System.Web para acessar também o contexto atual, acessar Serverou os primeiros objetos mencionados

NB: estou direcionando a versão 4.6 do framework (Mvc5)


4
Se você tentar chamar uma ação no controlador que use View (..) ou PartialView (...), precisará alterar manualmente os routeData, para que o ASP.NET saiba como encontrar sua exibição. controller.RouteData.Values["controller"] = "Home";controller.RouteData.Values["action"] = "Index";Supondo que você esteja tentando retornar o resultado da ação Índice no HomeController.
Steven

@ Steven Eu tive que aplicar esses valores em thisvez de controller. Em última análise, o resultado volta através do controlador local (isto) e é isso que acaba tentando encontrar a visualização.
Aaaantoine

Eu acrescentaria também que a propriedade Url não é inicializada em DependencyResolver.Current.GetService <ControllerB> (). Então você tem que copiá-lo do controlador atual manualmente.
Ralfeus 7/02

Na ação de segmentação, você deve usar return View("ViewName");apenas #return View();
mNejkO 12/17

9

Deixe o resolvedor fazer isso automaticamente.

Um controlador interno:

public class AController : ApiController
{
    private readonly BController _bController;

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

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

}

2
Para obter a resposta mais limpa, você deve definir o contexto do controlador para o novo controlador.
Mafii 26/06

8

Se alguém estiver olhando para como fazer isso no núcleo .net, consegui adicionando o controlador na inicialização

services.AddTransient<MyControllerIwantToInject>();

Em seguida, injetando-o no outro controlador

public class controllerBeingInjectedInto : ControllerBase
{
    private readonly MyControllerIwantToInject _myControllerIwantToInject

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

Então é só chamar assim _myControllerIwantToInject.MyMethodINeed();


4

Era exatamente isso que eu procurava depois de descobrir que RedirectToAction()não passaria objetos de classe complexos.

Como exemplo, desejo chamar o IndexComparisonmétodo no LifeCycleEffectsResultscontrolador e transmitir a ele um objeto de classe complexo chamado model.

Aqui está o código que falhou:

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

Vale a pena notar que Strings, números inteiros, etc. estavam sobrevivendo à viagem para esse método de controlador, mas objetos de lista genéricos estavam sofrendo com o que era remanescente dos vazamentos de memória C.

Conforme recomendado acima, aqui está o código que eu substituí por:

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

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

Tudo está funcionando como pretendido agora. Obrigado por liderar o caminho.


3

A resposta de Dleh está correta e explica como obter uma instância de outro controlador sem falta de dependências configuradas para IoC

No entanto, agora precisamos chamar o método desse outro controlador.
A resposta completa seria:

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

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

Como você chama a ação "MethodNameFromControllerB_ToCall" se espera parâmetros? por exemplo MethodNameFromControllerB_ToCall (int somenum, string someetext)?
Patee Gutee

3

Eu sei que é velho, mas você pode:

  • Crie uma camada de serviço
  • Mover método para lá
  • Método de chamada nos dois controladores

2

se o problema for ligar. você pode chamá-lo usando esse método.

yourController obj= new yourController();

obj.yourAction();

1
Pfft! E se você estiver esperando um resultado de uma ação? var res = new ControllerB().SetUpTimer(new TimeSpan(23, 20, 00));
DirtyBit
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.