188

I need to call a controller B action FileUploadMsgView from Controller A and need to pass a parameter for it.

Its not going to the controller B's FileUploadMsgView().

Here's the code:

ControllerA:

private void Test()
{
    try
    {   //some codes here
        ViewBag.FileUploadMsg = "File uploaded successfully.";
        ViewBag.FileUploadFlag = "2";
        RedirectToAction("B", "FileUploadMsgView", new { FileUploadMsg = "File   uploaded successfully" });
    }
}

ControllerB (receiving part):

public ActionResult FileUploadMsgView(string FileUploadMsg)
{
    return View();
}
Himanshu
  • 31,810
  • 31
  • 111
  • 133
user2156088
  • 2,350
  • 5
  • 20
  • 24
  • 4
    I know this question is old but in my opinion you should mark the answer from ed chapel as the best one, tieson's looks like a hack, it's still valid, but why use a workaround when you can use it the way it was meant to be and get the desired result – Anders M. Feb 28 '14 at 14:00
  • 2
    @AndersM. Ed's answer does a redirect. That is not what I want when I found this question searching for a solution. – mxmissile Dec 12 '14 at 17:07
  • @mxmissile not to be a dick but Ed's answer is what the asker needs since he wants a view that is returned based on what is uploaded, I agree that asker could have done a better job at formulating his question(is this the right word?) we can't know this though as his english may be limited, even though Tiesons answer helped you - which is good - it doesn't change the fact that Ed's answer best reflects what the asker needs – Anders M. Dec 16 '14 at 15:16
  • 2
    @AndersM. I understand, my comment wording was just bad... :-) I should have emphasized the point that was not the result *I* desired. – mxmissile Dec 16 '14 at 16:30
  • 1
    @AndersM. The asker accepted the answer of Tieson as best, so I'm not sure why you would decide for him? The answer Tieson gave me helped me more then the answer Ed's answer. SO is not just for helping a single person, but everyone who has similar problems. So why not just keep Tieson's answer on top? – Kevin Dec 19 '14 at 13:57
  • defies the concept of mvc! – Stephen Mudere Aug 03 '17 at 07:06

11 Answers11

253

As @mxmissile says in the comments to the accepted answer, you shouldn't new up the controller because it will be missing dependencies set up for IoC and won't have the HttpContext.

Instead, you should get an instance of your controller like this:

var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);
JoeSharp
  • 405
  • 1
  • 7
  • 16
DLeh
  • 23,806
  • 16
  • 84
  • 128
  • Exactly what I was looking for. Note that those not using IoC still won't get an `HttpContext` injected. – brichins Aug 28 '15 at 21:34
  • `var controller` would be assigned the type `ControllerB`, yes. – DLeh Feb 02 '16 at 14:58
  • Adding the controllerContext to the new instance of the controller would be important in some cases. I am posting a complemetary answer. – Nishanth Shaan May 09 '16 at 15:19
  • 1
    This gets me close, but one problem that arises is that in my case, controller.MyAction() makes reference to User.Identity which appears to be uininstantiated. – Robert H. Bourdeau May 25 '16 at 17:10
  • @brichins Can you be more explicit when you say 'those not using IoC'? I'm curious what specifically is involved/available here for choosing to use inversion of control in this context, and therefore under what circumstances one would 'still not get an HttpContext injected. – ilasno Dec 11 '16 at 17:55
  • 1
    @ilasno I'm rusty on MVC these days, but I think I meant that you have to actually *have* IoC set up to get a fully populated Controller object (e.g. an associated `HttpContext`). I believe I used this approach *without* any IoC to get a "shallow" controller object (just needed access to certain functionality) and was initially confused about why parts were "missing". [aside: I worked around it while still using this approach, but probably should have refactored that functionality out to a shared class.] As for IoC setup and choices, I'd have to refer you to other articles / SO questions. – brichins Dec 12 '16 at 19:59
  • @brichins I guess i'm mainly concerned with HttpContext, and am assuming (and testing now :-D) that by using the lines indicated in this answer, the controller instantiated will have access to the HttpContext. I don't have experience using IoC to inject dependencies through a Controller constructor. – ilasno Dec 12 '16 at 20:46
  • In the targeting Action you should use `return View("ViewName");` instead just `return View();` – mNejkO Mar 12 '17 at 22:38
  • `ModelState` is not forwarded to the sub controller => it will always be empty and valid... which is a problem. – Yepeekai Apr 07 '17 at 19:58
  • 3
    Some people get carried away with pointless edits... note that someone edited the answer changing the variable "controller" to "ctrlr"... so it should read "ctrlr.ControllerContext = new ControllerContext(this.Request.RequestContext, ctrl);" if that user edited it correctly – JoeSharp Dec 27 '17 at 15:10
123

Controllers are just classes - new one up and call the action method just like you would any other class member:

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

Tieson T.
  • 20,774
  • 6
  • 77
  • 92
  • 92
    Won't you be missing ControllerContext, Request and friends if you just do this? – cirrus Oct 15 '13 at 10:37
  • 1
    @cirrus Probably. Depends on whether you need anything other than the result from the controller action. For this specific question, I think the OP just wanted the rendered view from the method. – Tieson T. Oct 16 '13 at 07:28
  • 30
    The instantiation of controller is not a good idea because it's life cycle might be controlled by another part of the application. E.g. when using an IoC container all depdencies should be injected, etc. – Mo Valipour Nov 25 '13 at 16:37
  • 1
    @Valipour That would be a valid point if the OP was using IoC, but you can't assume that from the code shown. I would think Ed Chapel's answer avoids that issue, though. – Tieson T. Nov 25 '13 at 19:12
  • 57
    If your using IoC, you can get a populated controller via `var controller = DependencyResolver.Current.GetService();` – mxmissile Dec 12 '14 at 17:11
  • 9
    @mxmissile That's worth adding as a new answer, rather than a comment here. – Tieson T. Dec 13 '14 at 03:15
  • @TiesonT. - agreed. That would be the preferred option as oppose to new'ing another controller up. –  Feb 09 '16 at 11:49
  • @mxmissile What specifically does 'using IoC' mean in this context? – ilasno Dec 11 '16 at 17:59
  • 3
    @ilasno Are you familiar with the term "inversion of control"? The point he's making is that if you have components in your controllers that need to be injected into the constructor, my answer doesn't really work, unless you use something like the DependencyResolver as a service-locator. – Tieson T. Dec 11 '16 at 18:26
  • @mxmissile i am familiar, but do not have experience with injecting dependencies into a Controller specifically. I'm testing with DLeh's answer below that utilizes DependencyResolver. – ilasno Dec 12 '16 at 20:49
  • If you call an action method this way, you'll bypass all the action filters and other behaviors that are defined via attribute. – John Wu Nov 12 '17 at 20:30
  • I tried this and I cannot get the new view to show up – superfly71 Apr 06 '18 at 13:16
  • can't do this in nopcommerce, controller is not visible – Dinuka Salwathura Dec 29 '20 at 10:50
  • How to dispose the instance? – variable Jan 11 '22 at 16:02
  • @variable If you're talking about the controller, as shown in the example code, it should go out of scope pretty much immediately and be automatically disposed. Otherwise, wrap it in a using block. – Tieson T. Jan 13 '22 at 22:15
64

Your sample looks like psuedo code. You need to return the result of RedirectToAction:

return RedirectToAction("B", 
                        "FileUploadMsgView",
                        new { FileUploadMsg = "File uploaded successfully" });
Paul Fleming
  • 24,238
  • 8
  • 76
  • 113
Ed Chapel
  • 6,842
  • 3
  • 30
  • 44
17

as @DLeh says Use rather

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

But, giving the controller, a controlller context is important especially when you need to access the User object, Server object, or the HttpContext inside the 'child' controller.

I have added a line of code:

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

or else you could have used System.Web to acces the current context too, to access Server or the early metioned objects

NB: i am targetting the framework version 4.6 (Mvc5)

Nishanth Shaan
  • 1,422
  • 15
  • 18
  • 6
    If you try to call an action in the controller which uses View(..) or PartialView(...) you need to manually change the routeData, so that ASP.NET knows how to find your view. `controller.RouteData.Values["controller"] = "Home";controller.RouteData.Values["action"] = "Index";` Assuming you are trying to return the result from the Index action in HomeController. – Steven Jun 18 '16 at 01:28
  • @Steven I had to apply these values to `this` rather than `controller`. Ultimately the result comes back through the local controller (this) so that's what ends up trying to find the view. – aaaantoine Aug 29 '16 at 13:36
  • 1
    I'd add also that Url property isn't initialized upon DependencyResolver.Current.GetService(). So you have to copy it from current controller manually. – Ralfeus Feb 07 '17 at 10:35
  • In the targeting Action you should use `return View("ViewName");` instead just `return View();` – mNejkO Mar 12 '17 at 23:52
12

Let the resolver automatically do that.

Inside A controller:

public class AController : ApiController
{
    private readonly BController _bController;

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

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

}
David Castro
  • 1,773
  • 21
  • 21
12

If anyone is looking at how to do this in .net core I accomplished it by adding the controller in startup

services.AddTransient<MyControllerIwantToInject>();

Then Injecting it into the other controller

public class controllerBeingInjectedInto : ControllerBase
{
    private readonly MyControllerIwantToInject _myControllerIwantToInject

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

Then just call it like so _myControllerIwantToInject.MyMethodINeed();

Leonardo Wildt
  • 2,529
  • 5
  • 27
  • 56
  • 1
    seems ok for .NET Core 2.1, but does not fit so well for .NET Core 3.1 (and probably upcoming .NET 5) where the controllers are added with [AddControllers](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.mvcservicecollectionextensions.addcontrollers) I am still looking for the cleanest solution for .NET 3.1... – EricBDev Aug 17 '20 at 16:24
  • .NET 6 status: `builder.Services.AddTransient();` //working `builder.Services.AddControllers();`//not working – The scion Jan 24 '23 at 07:25
12

I know it's old, but you can:

  • Create a service layer
  • Move method there
  • Call method in both controllers
Watth
  • 406
  • 5
  • 12
7

This is exactly what I was looking for after finding that RedirectToAction() would not pass complex class objects.

As an example, I want to call the IndexComparison method in the LifeCycleEffectsResults controller and pass it a complex class object named model.

Here is the code that failed:

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

Worth noting is that Strings, integers, etc were surviving the trip to this controller method, but generic list objects were suffering from what was reminiscent of C memory leaks.

As recommended above, here's the code I replaced it with:

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

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

All is working as intended now. Thank you for leading the way.

Andrei
  • 42,814
  • 35
  • 154
  • 218
cghore
  • 101
  • 1
  • 3
6

Dleh's answer is correct and explain how to get an instance of another controller without missing dependencies set up for IoC

However, we now need to call the method from this other controller.
Full answer would be :

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

//Call your method
ActionInvoker.InvokeAction(controller.ControllerContext, "MethodNameFromControllerB_ToCall");
AlexB
  • 7,302
  • 12
  • 56
  • 74
  • 1
    How do you call action "MethodNameFromControllerB_ToCall" if it expects parameters? for example MethodNameFromControllerB_ToCall(int somenum, string sometext)? – Patee Gutee Dec 13 '18 at 15:14
5

if the problem is to call. you can call it using this method.

yourController obj= new yourController();

obj.yourAction();
  • 2
    Pfft! What if you're expecting a result from an action instead? `var res = new ControllerB().SetUpTimer(new TimeSpan(23, 20, 00));` – DirtyBit Mar 29 '18 at 09:49
  • Just because they didn't parse the return value doesn't mean it's not a valid answer. – Zimano Sep 03 '21 at 09:42
4

If you use .NET Core or .NET 5 < you can do it like this:

MVC:

services.AddMvc().AddControllersAsServices();

ApiController:

services.AddControllers().AddControllersAsServices();

Then you can simply inject your controller like any other service

Ogglas
  • 62,132
  • 37
  • 328
  • 418