3

My goal is to find a controller from it's name and area. I have successfully done this if my current httpContext is within the same Area as the to-be-found controller. However, I cannot get my call to the ControllerFactory to take Area into consideration. Here's my code:

public static ControllerBase GetControllerByName(this HtmlHelper htmlHelper, string controllerName)
    {
      IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
      IController controller = factory.CreateController(htmlHelper.ViewContext.RequestContext, controllerName);
      if (controller == null)
      {
        throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "The IControllerFactory '{0}' did not return a controller for the name '{1}'.", factory.GetType(), controllerName));
      }
      return (ControllerBase)controller;
    }

Since it's taking a RequestContext as a parameter, I've added an route value of "area" to it but with no change. Is there something I can do with the requestContext to some how take area into consideration? Do I need to override the controller factory--and if so, what in particular handles Area distinction?

Update:

Here is an example of a AreaRegistration I have:

public class StoresAreaRegistration : AreaRegistration
  {
    public override string AreaName { get { return "Stores"; } }

    public override void RegisterArea(AreaRegistrationContext context)
    {
      context.MapRoute(
          AreaName,
          AreaName + "/{controller}/{action}/{id}",
          new { area = AreaName, controller = "Home", action = "Index", id = UrlParameter.Optional }
      );
    }
  }
9
  • Have you tried with RequestContext.RouteData? Commented Jan 30, 2013 at 19:06
  • @cheesemacfly, Yes -- htmlHelper.ViewContext.RequestContext.RouteData.DataTokens["area"] = areaName and htmlHelper.ViewContext.RequestContext.RouteData.Values["area"] = areaName; were both tried with no success. Commented Jan 30, 2013 at 19:07
  • And how did you declare this route in your Global.asax? Commented Jan 30, 2013 at 19:14
  • Well the route is actually declared in the AreaRegistration.cs file. I've added it to the question. Commented Jan 30, 2013 at 19:19
  • You don't have access to the Request.RequestContext object in your GetControllerByName() function? Commented Jan 30, 2013 at 19:52

1 Answer 1

11

The Area and Namespaces used to locate a controller are in the RouteData of the RequestContext. They are populatd by default based off of the request you are currently serving up, if you need to change them you have to do so before calling CreateController. You may get an exception when a controller cannot be found so you'll have to account for that as well.

UPDATE: Note, you MUST create a new RequestContext. If you reuse the existing one it will mess with the resolution of actions & views later on down the line in this request.

var tempRequestContext = new RequestContext(Request.RequestContext.HttpContext, new RouteData());
tempRequestContext.RouteData.DataTokens["Area"] = "";
tempRequestContext.RouteData.DataTokens["Namespaces"] = "YourCompany.Controllers";
var controller = ControllerBuilder.Current.GetControllerFactory()
                .CreateController(tempRequestContext, "ControllerName");

if(controller != null)
{
    //TODO: Implement your logic here
}
Sign up to request clarification or add additional context in comments.

4 Comments

Hi, slightly different error and this answer was what I was looking for although I have to changes ["Namespaces"] for ["namespaces"] (haven't used areas, so I don't know if that key requires lower case as well). Thanks
I use a capital in mine and it works fine, I'm not sure why you would have different results with a caps vs lowercase.
In MVC 5 apparently the Namespaces value expects an IEnumerable<string> instead of just a string. so use : tempRequestContext.RouteData.DataTokens["Namespaces"] = new List<string> { "YourCompany.Controllers" };
It should accept both. If you're only passing in only one namespace, you can just give it the string. You don't have to pass it in wrapped in a list. The type expected by DataTokens["Namespaces"] is of type object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.