tags:

views:

294

answers:

3

I'm building a controller that other controllers can inherit (provide base functionality across site without repeating code):

public abstract class ApplicationController : Controller
{
    protected ApplicationController()
    {
       //site logic goes here
       //what is the value of agentID from the Action below??
    }
}

public class AgentController : ApplicationController
{
    public ActionResult Index(string agentID)
    {
        return View();
    }
}

The logic that applies to the entire site will go into the constructor of the ApplicationController class.

The problem is in that constructor I need to access the value in the parameter from the Action, in this case agentID (it will be the same across the entire site). Is there a way to read that value in?

A: 

There is, though you need to override the ControllerFactory. In your RegisterRoutes method in global.asax.cs, add this line:

public static void RegisterRoutes(RouteCollection routes) {
    // Route code goes here
    ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
}

And then define your MyControllerFactory class

public class MyControllerFactory : DefaultControllerFactory {
    public override IController CreateController(RequestContext requestContext, string controllerName) {
        // poke around the requestContext object here
        return base.CreateController(requestContext, controllerName);
    }
}

The requestContext object has all of the route data and values in it. You can use this to pass whatever you would like to the constructor of your controller.

Edit to add that this is how most of the popular Dependency Injectors (for example, StructureMap) work too.

swilliams
+2  A: 

Actions happen after constructors. The value doesn't exist (hasn't been bound) in a constructor. The route data might be known in the constructor, but the action data binding certainly will not have happened yet. You can't get this value with certainty until the action has been invoked.

Route data can be accessed inside the controller via:

ControllerContext.RouteData.Values

However, it is incorrect to suppose that agentID can only be bound to route data. In fact, it could come from a server variable, a form field, a query string parameter, etc. My advice would be to pass it explicitly wherever it is needed. If nothing else, it makes your unit tests better.

Craig Stuntz
Nope, won't work. It works in the original controller (AgentController in my example), but not in the one being inherited from (ApplicationController). It comes back as object not being set.
Carlton Jenke
As far as the second part of your comment - that is why this is a problem. You cannot pass a parameter that is defined in the Action to a constructor of an inherited class; it has to be able to access the value from somewhere else.
Carlton Jenke
<continued> The only other way to do it I see it to not use the constructor, but to use method calls; I don't want to do it that way, I'd rather the inherited class automatically drive the logic with no calls needed.
Carlton Jenke
Actions happen *after* constructors. The value doesn't exist (hasn't been bound) when you want it. You *can't* get this value with certainty until the action has been invoked.
Craig Stuntz
A: 

I figured out how to do it ... very similar to Craig Stuntz's answer, but the difference is in how you reach the RouteData.

Using ControllerContext.RouteData.Values does not work in a regular method used this way (it does from the original controller, but not from a base one like I built), but I did get to the RouteData by overriding the OnActionExecuting method:

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        string agentID = filterContext.RouteData.Values["agentID"].ToString();
        OtherMethodCall(agentID);
    }
Carlton Jenke