1

I have an ActionDescriptor from which I retrieve information about an action and its controller:

ActionDescriptor desc = ...;
string action = desc.ActionName;
string controller = desc.ControllerDescriptor.ControllerName;
string area = ?;

I'm wondering if there is a better way to determine the controller's area than having to parse its namespace, which I'm currently doing like so:

// e.g., Company.Areas.Foo.Controllers
var parts = desc.ControllerDescriptor.ControllerType.Namespace.Split('.').ToList();
var areaIndex = parts.IndexOf("Areas");
if (areaIndex > -1) area = parts[areaIndex + 1];
// area = "Foo"

EDIT:

I should clarify that I'm not in the context of a view or controller and am trying to determine the area only given its Type information and such.

Omer Bokhari
  • 57,458
  • 12
  • 44
  • 58

3 Answers3

2

ASP.NET MVC doesn't keep the AreaRegistration objects around, but AreaRegistration is how ASP.NET determines which Area a Controller belongs to.

You can just enumerate AreaRegistration types on your own within the relevant assemblies, then determine if the AreaRegistration's namespace is a prefix of the Controller's namespace. Once resolved, an AreaRegistration instance has the Area name as a property.

Rather than using reflection, you could include this in your AreaRegistration implementations themselves by providing a base class that implements RegisterArea and maintains a Namespace=>Area map.

Mike Haboustak
  • 2,266
  • 1
  • 18
  • 18
2

Here's what I've scrambled together to get this working for every controller:

private string findAreaForControllerType(Type controllerType)
{
    var areaTypes = getAllAreasRegistered();

    foreach (var areaType in areaTypes)
    {
        if (controllerType.Namespace.StartsWith(areaType.Namespace))
        {
            var area = (AreaRegistration)Activator.CreateInstance(areaType);
            return area.AreaName;
        }
    }
    return string.Empty;
}

private IEnumerable<Type> getAllAreasRegistered()
{
    var assembly = getWebEntryAssembly();
    var areaTypes = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(AreaRegistration)));

    return areaTypes;
}

static private Assembly getWebEntryAssembly()
{
    if (System.Web.HttpContext.Current == null ||
        System.Web.HttpContext.Current.ApplicationInstance == null)
    {
        return null;
    }

    var type = System.Web.HttpContext.Current.ApplicationInstance.GetType();
    while (type != null && type.Namespace == "ASP")
    {
        type = type.BaseType;
    }

    return type == null ? null : type.Assembly;
}

This fetches all the AreaRegistration types from the Web project assembly and matches the namespace of your controller to the namespace of the area. I.e. When you have a controller of type My.WebProject.Areas.Admin.UserController the system will return the Admin area based on the match of namespace My.WebProject.Areas.Admin.

Peter
  • 14,221
  • 15
  • 70
  • 110
0

From a view:

ViewContext.RouteData.DataTokens["area"]

From a controller:

ControllerContext.RouteData.DataTokens["area"]
Andy T
  • 10,223
  • 5
  • 53
  • 95
  • 1
    it may not be clear from the question, but this answer is not relevant. i'm not trying to determine the area of the controller currently in context, but the area of any given controller type. – Omer Bokhari Aug 20 '13 at 16:32
  • I was able to get the area from the controller but did so with: HttpContext.Current.Request.RequestContext.RouteData.DataTokens["area"].ToString().ToLower(); – MattParra Sep 25 '15 at 19:11
  • 1
    For those interested, for views, ViewContext.RouteData.DataTokens["area"] seems to have become ViewContext.RouteData.Values["area"] in recent versions in MVC. Or at least for me :) – AFract Sep 18 '18 at 09:59