Jak wywołać inny kontroler Action From a controller in Mvc

153

Muszę wywołać akcję kontrolera B FileUploadMsgView z kontrolera A i muszę przekazać dla niej parametr.

 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
źródło
3
Wiem, że to pytanie jest stare, ale moim zdaniem powinieneś oznaczyć odpowiedź z ed Chapel jako najlepszą, Tieson wygląda jak hack, nadal jest aktualny, ale po co stosować obejście, skoro można go używać tak, jak powinno i uzyskaj pożądany rezultat
Anders M.
1
@AndersM. Odpowiedź Eda powoduje przekierowanie. Nie tego chcę, kiedy znalazłem to pytanie w poszukiwaniu rozwiązania.
mxmissile
@mxmissile nie jest kutasem, ale odpowiedź Eda jest tym, czego potrzebuje pytający, ponieważ chce widoku, który jest zwracany na podstawie tego, co zostało przesłane, zgadzam się, że pytający mógł wykonać lepszą pracę przy formułowaniu swojego pytania (czy to właściwe słowo? ) nie możemy tego wiedzieć, ponieważ jego angielski może być ograniczony, chociaż odpowiedź Tiesona pomogła ci - co jest dobre - nie zmienia to faktu, że odpowiedź Eda najlepiej odzwierciedla to, czego potrzebuje pytający
Anders M.
2
@AndersM. Rozumiem, mój komentarz był po prostu zły ... :-) Powinienem był podkreślić punkt, który nie był wynikiem, którego oczekiwałem.
mxmissile
@AndersM. Pytający przyjął odpowiedź Tiesona jako najlepszą, więc nie jestem pewien, dlaczego zdecydowałbyś za niego? Odpowiedź, której udzielił mi Tieson, pomogła mi bardziej niż odpowiedź Eda. SO nie służy tylko do pomagania pojedynczej osobie, ale każdemu, kto ma podobne problemy. Dlaczego więc nie pozostawić odpowiedzi Tiesona na pierwszym miejscu?
Kevin Voorn

Odpowiedzi:

106

Kontrolery to po prostu klasy - nowe w górę i wywołaj metodę akcji, tak jak każdy inny członek klasy:

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

Tieson T.
źródło
76
Czy nie przegapisz ControllerContext, Request i przyjaciół, jeśli po prostu to zrobisz?
cirrus
20
Tworzenie instancji kontrolera nie jest dobrym pomysłem, ponieważ jego cykl życia może być kontrolowany przez inną część aplikacji. Np. Podczas korzystania z kontenera IoC należy wstrzyknąć wszystkie depensy itp.
Mo Valipour
48
Jeśli używasz IoC, możesz uzyskać wypełniony kontroler przezvar controller = DependencyResolver.Current.GetService<ControllerB>();
mxmissile
3
@mxmissile Warto to dodać jako nową odpowiedź, zamiast komentarza tutaj.
Tieson T.
2
@ilasno Czy znasz termin „odwrócenie kontroli”? Chodzi o to, że jeśli masz komponenty w kontrolerach, które muszą zostać wstrzyknięte do konstruktora, moja odpowiedź tak naprawdę nie działa, chyba że używasz czegoś takiego jak DependencyResolver jako lokalizator usług.
Tieson T.,
202

Jak @mxmissile mówi w komentarzach do zaakceptowanej odpowiedzi, nie powinieneś aktualizować kontrolera, ponieważ będzie brakował zależności skonfigurowanych dla IoC i nie będzie miał HttpContext.

Zamiast tego powinieneś otrzymać taką instancję kontrolera:

var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);
DLeh
źródło
Dokładnie to, czego szukałem. Zauważ, że ci, którzy nie używają IoC, nadal nie zostaną HttpContextwstrzyknięci.
brichins
var controllerzostanie przypisany typ ControllerB, tak.
DLeh
1
To mnie zbliża, ale jeden problem, który się pojawia, polega na tym, że w moim przypadku controller.MyAction () odwołuje się do User.Identity, która wydaje się być niezainstalowana.
Robert H. Bourdeau
1
@ilasno Jestem zardzewiały na MVC te dni, ale myślę, że oznaczało, że trzeba rzeczywiście mieć IoC skonfigurować, aby uzyskać w pełni zaludnionych obiektu Controller (np skojarzony HttpContext). Wydaje mi się, że zastosowałem to podejście bez żadnego IoC, aby uzyskać „płytki” obiekt kontrolera (po prostu potrzebowałem dostępu do pewnych funkcji) i początkowo nie miałem pojęcia, dlaczego „brakuje” części. [na marginesie: obejrzałem to, wciąż stosując to podejście, ale prawdopodobnie powinienem był przekształcić tę funkcjonalność w klasę współdzieloną]. Jeśli chodzi o konfigurację IoC i wybory, musiałbym skierować cię do innych artykułów / pytań SO.
brichins
3
Niektórzy ludzie dają się ponieść bezsensownym edycjom ... zauważ, że ktoś zmienił odpowiedź, zmieniając zmienną "controller" na "ctrlr" ... więc powinna brzmieć "ctrlr.ControllerContext = new ControllerContext (this.Request.RequestContext, ctrl) ; " jeśli ten użytkownik edytował go poprawnie
JoeSharp
62

Twoja próbka wygląda jak kod pseudo. Musisz zwrócić wynik RedirectToAction:

return RedirectToAction("B", 
                        "FileUploadMsgView",
                        new { FileUploadMsg = "File uploaded successfully" });
Ed Chapel
źródło
4
Należy zauważyć, że jeśli akcja docelowa akceptuje tylko POST, to nie zadziała.
Marco Alves,
13
Zwraca to 302, co powoduje kolejne trafienie na serwer, co nie jest tym, o co chodzi w pytaniu.
rboarman
16

jak mówi @DLeh Użyj raczej

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

Jednak przekazanie kontrolerowi kontekstu kontrolera jest ważne, zwłaszcza gdy potrzebujesz uzyskać dostęp do Userobiektu, Serverobiektu lub HttpContextwnętrza kontrolera „podrzędnego”.

Dodałem linię kodu:

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

w przeciwnym razie mógłbyś użyć System.Web, aby uzyskać dostęp do bieżącego kontekstu, aby uzyskać dostęp Serverdo obiektów, które zostały wcześniej wymienione

Uwaga: celuję w platformę w wersji 4.6 (Mvc5)

Nishanth Shaan
źródło
4
Jeśli spróbujesz wywołać akcję w kontrolerze, który używa View (..) lub PartialView (...), musisz ręcznie zmienić routeData, aby ASP.NET wiedział, jak znaleźć Twój widok. controller.RouteData.Values["controller"] = "Home";controller.RouteData.Values["action"] = "Index";Zakładając, że próbujesz zwrócić wynik z akcji Index w HomeController.
Steven
@Steven Musiałem zastosować te wartości thiszamiast controller. Ostatecznie wynik wraca przez lokalny kontroler (ten), więc to właśnie kończy się próbą znalezienia widoku.
aaaantoine
Dodałbym również, że właściwość Url nie jest inicjowana po DependencyResolver.Current.GetService <ControllerB> (). Musisz więc ręcznie skopiować go z bieżącego kontrolera.
Ralfeus,
W akcji celowania należy return View("ViewName");zamiast tego użyć po prostureturn View();
mNejkO
9

Pozwól resolverowi to zrobić automatycznie.

Wewnątrz kontrolera:

public class AController : ApiController
{
    private readonly BController _bController;

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

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

}
David Castro
źródło
2
imo to najczystsza odpowiedź, ale powinieneś ustawić kontekst kontrolera na nowy kontroler.
Mafii
8

Jeśli ktoś zastanawia się, jak to zrobić w .net core, udało mi się to przez dodanie kontrolera podczas uruchamiania

services.AddTransient<MyControllerIwantToInject>();

Następnie wstrzyknięcie go do innego kontrolera

public class controllerBeingInjectedInto : ControllerBase
{
    private readonly MyControllerIwantToInject _myControllerIwantToInject

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

Następnie tak to nazwij _myControllerIwantToInject.MyMethodINeed();

Leonardo Wildt
źródło
4

To jest dokładnie to, czego szukałem po znalezieniu tego RedirectToAction() że nie przejdzie przez złożone obiekty klas.

Na przykład chcę wywołać IndexComparisonmetodę wLifeCycleEffectsResults kontrolerze i przekazać jej obiekt klasy złożonej o nazwie model.

Oto kod, który się nie powiódł:

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

Warto zauważyć, że ciągi znaków, liczby całkowite itp. Przetrwały podróż do tej metody kontrolera, ale ogólne obiekty listy cierpiały z powodu czegoś, co przypominało wycieki pamięci C.

Zgodnie z powyższymi zaleceniami, oto kod, który zastąpiłem:

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

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

Teraz wszystko działa zgodnie z przeznaczeniem. Dziękuję za wytyczenie drogi.

cghore
źródło
3

Odpowiedź Dleha jest poprawna i wyjaśnij, jak uzyskać instancję innego kontrolera bez brakujących zależności skonfigurowanych dla IoC

Jednak teraz musimy wywołać metodę z tego innego kontrolera.
Pełna odpowiedź brzmiałaby:

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

//Call your method
ActionInvoker.InvokeAction(controller.ControllerContext, "MethodNameFromControllerB_ToCall");
AlexB
źródło
Jak wywołać akcję „MethodNameFromControllerB_ToCall”, jeśli oczekuje ona parametrów? na przykład MethodNameFromControllerB_ToCall (int somenum, string somext)?
Patee Gutee
3

Wiem, że jest stary, ale możesz:

  • Utwórz warstwę usług
  • Przenieś tam metodę
  • Metoda wywołania w obu kontrolerach
Watth
źródło
2

jeśli problemem jest zadzwonić. możesz to nazwać za pomocą tej metody.

yourController obj= new yourController();

obj.yourAction();

źródło
1
Pfft! A co, jeśli zamiast tego spodziewasz się wyniku działania? var res = new ControllerB().SetUpTimer(new TimeSpan(23, 20, 00));
DirtyBit