Как вызвать другой контроллер Действие С контроллера в Mvc

153

Мне нужно вызвать контроллер B действия FileUploadMsgView из контроллера A и нужно передать параметр для него.

 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
источник
3
Я знаю, что этот вопрос старый, но, на мой взгляд, вы должны пометить ответ от ed chapel как лучший, tieon выглядит как взлом, он все еще действителен, но зачем использовать обходной путь, если вы можете использовать его так, как он должен был быть и получить желаемый результат
Андерс М.
1
@AndersM. Эд ответ делает редирект. Это не то, что я хочу, когда я нашел этот вопрос в поисках решения.
mxmissile
@mxmissile не быть членом, но ответ Эда - то, что нужно спрашивающему, так как он хочет, чтобы представление, возвращаемое на основе того, что было загружено, я согласен, что он мог бы лучше справиться с формулировкой своего вопроса (это правильное слово? ) мы не можем знать это, хотя его английский может быть ограничен, хотя ответ Тионсона помог вам - что хорошо - это не меняет того факта, что ответ Эда лучше всего отражает то, что нужно спрашивающему
Андерс М.
2
@AndersM. Я понимаю, моя формулировка комментария была просто плохой ... :-) Я должен был подчеркнуть пункт, который не был результатом, которого я желал.
mxmissile
@AndersM. Аскер принял ответ Тисона как лучший, так что я не уверен, почему вы решили за него? Ответ, который дал мне Тизон, помог мне больше, чем ответ Эда. SO не только для того, чтобы помочь одному человеку, но и для всех, у кого есть подобные проблемы. Так почему бы просто не держать ответ Тисона на вершине?
Кевин Воорн

Ответы:

106

Контроллеры - это просто классы - новый и вызов метода действия, как и любой другой член класса:

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

Тисон Т.
источник
76
Не скучаете ли вы по ControllerContext, Request и друзьям, если вы просто делаете это?
циррус
20
Создание экземпляра контроллера не очень хорошая идея, поскольку его жизненный цикл может контролироваться другой частью приложения. Например, при использовании контейнера IoC все инъекции должны быть введены и т. Д.
Мо Валипур
48
Если вы используете IoC, вы можете получить заполненный контроллер черезvar controller = DependencyResolver.Current.GetService<ControllerB>();
mxmissile 12.12.14
3
@mxmissile Это стоит добавить как новый ответ, а не как комментарий здесь.
Тисон Т.
2
@ilasno Вы знакомы с термином "инверсия контроля"? Смысл его в том, что если у вас есть компоненты в ваших контроллерах, которые нужно внедрить в конструктор, мой ответ на самом деле не сработает, если вы не используете что-то вроде DependencyResolver в качестве сервис-локатора.
Tieson T.
202

Как говорит @mxmissile в комментариях к принятому ответу, вы не должны обновлять контроллер, поскольку в нем будут отсутствовать зависимости, установленные для IoC, и не будет иметь HttpContext.

Вместо этого вы должны получить экземпляр вашего контроллера следующим образом:

var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);
DLeh
источник
Именно то, что я искал. Обратите внимание, что те, кто не использует IoC, все равно не получат HttpContextинъекцию.
бричины
var controllerбудет назначен тип ControllerB, да.
DLeh
1
Это сближает меня, но возникает одна проблема: в моем случае, controller.MyAction () ссылается на User.Identity, который кажется необоснованным.
Роберт Х. Бурдо
1
@ilasno Я ржавый на MVC в эти дни, но я думаю , что я имел в виду , что у вас есть на самом деле есть IoC настроить , чтобы получить полностью заселен объект контроллера (например , связанный с ним HttpContext). Я полагаю, что использовал этот подход без какого-либо IoC, чтобы получить «неглубокий» объект контроллера (просто необходим доступ к определенным функциям), и изначально был озадачен, почему детали «отсутствовали». [в сторону: я работал над этим, все еще используя этот подход, но, вероятно, следовало бы переориентировать эту функциональность на общий класс.] Что касается настройки и выбора IoC, мне придется обратиться к другим статьям / таким вопросам.
Brichins
3
Некоторые люди увлекаются бессмысленными правками ... обратите внимание, что кто-то отредактировал ответ, изменив переменную "controller" на "ctrlr" ... поэтому он должен читать "ctrlr.ControllerContext = new ControllerContext (this.Request.RequestContext, ctrl) ;» если этот пользователь отредактировал это правильно
JoeSharp
62

Ваш пример выглядит как код psuedo. Вам необходимо вернуть результат RedirectToAction:

return RedirectToAction("B", 
                        "FileUploadMsgView",
                        new { FileUploadMsg = "File uploaded successfully" });
Эд Чапел
источник
4
Следует отметить, что если целевое действие принимает только POST, это не сработает.
Марко Алвес
13
Это возвращает 302, который вызывает другое попадание на сервер, что не то, что задает вопрос.
rboarman
16

как говорит @DLeh, используйте скорее

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

Но, учитывая контроллер, контекст контроллера важен, особенно когда вам нужен доступ к Userобъекту, Serverобъекту или HttpContextвнутри «дочернего» контроллера.

Я добавил строку кода:

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

иначе вы могли бы использовать System.Web для доступа к текущему контексту, для доступа Serverк ранним объектам

NB: я нацеливаюсь на версию фреймворка 4.6 (Mvc5)

Нишант Шаан
источник
4
Если вы попытаетесь вызвать действие в контроллере, который использует View (..) или PartialView (...), вам нужно вручную изменить routeData, чтобы ASP.NET знал, как найти ваше представление. controller.RouteData.Values["controller"] = "Home";controller.RouteData.Values["action"] = "Index";Предполагая, что вы пытаетесь вернуть результат действия Index в HomeController.
Стивен
@ Стивен Я должен был применить эти значения, thisа не controller. В конечном итоге результат возвращается через локальный контроллер (это), так что вот что в итоге пытается найти представление.
aaaantoine
Я бы добавил также, что свойство Url не инициализируется при DependencyResolver.Current.GetService <ControllerB> (). Таким образом, вы должны скопировать его из текущего контроллера вручную.
Ральфеус
В действии таргетинга вы должны использовать return View("ViewName");вместо этого простоreturn View();
mNejkO
9

Пусть распознаватель автоматически сделает это.

Внутри контроллера:

public class AController : ApiController
{
    private readonly BController _bController;

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

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

}
Дэвид Кастро
источник
2
IMO самый чистый ответ, но вы должны установить контекст контроллера для нового контроллера.
Мафия
8

Если кто-то смотрит, как это сделать в ядре .net, я сделал это, добавив контроллер при запуске.

services.AddTransient<MyControllerIwantToInject>();

Затем вводить его в другой контроллер

public class controllerBeingInjectedInto : ControllerBase
{
    private readonly MyControllerIwantToInject _myControllerIwantToInject

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

Тогда просто назови это так _myControllerIwantToInject.MyMethodINeed();

Леонардо Вильдт
источник
4

Это именно то, что я искал после того, как обнаружил, RedirectToAction()что не прошел бы объекты сложного класса.

В качестве примера я хочу вызвать IndexComparisonметод в LifeCycleEffectsResultsконтроллере и передать ему объект сложного класса с именем model.

Вот код, который не удалось:

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

Стоит отметить, что строки, целые числа и т. Д. Пережили поездку к этому методу контроллера, но объекты общего списка страдали от того, что напоминало утечки памяти C.

Как рекомендовано выше, вот код, который я заменил:

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

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

Все работает как задумано сейчас. Спасибо за лидерство.

cghore
источник
3

Ответ Длеха верен и объясняет, как получить экземпляр другого контроллера без отсутствующих зависимостей, настроенных для IoC

Однако теперь нам нужно вызвать метод из этого другого контроллера.
Полный ответ будет:

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

//Call your method
ActionInvoker.InvokeAction(controller.ControllerContext, "MethodNameFromControllerB_ToCall");
AlexB
источник
Как вы называете действие «MethodNameFromControllerB_ToCall», если оно ожидает параметры? например MethodNameFromControllerB_ToCall (int somenum, string sometext)?
Пати Гути
3

Я знаю, что он старый, но вы можете:

  • Создать сервисный слой
  • Переместить метод туда
  • Вызов метода в обоих контроллерах
Watth
источник
2

если проблема в том, чтобы позвонить. Вы можете вызвать это, используя этот метод.

yourController obj= new yourController();

obj.yourAction();

источник
1
Пфф! Что если вы ожидаете результата от действия вместо этого? var res = new ControllerB().SetUpTimer(new TimeSpan(23, 20, 00));
DirtyBit