9
votes

UPDATE 2

Ok - So it looks like my question is changing again slightly :-)

I've realised now that UrlHelper.Action doesn't seem to correctly resolve the URL in any Area unless the area name is explicitly specified. If it's not specified it seems to return whatever area name we're currently in which makes it look like it's working from one part of the site but then the same link in another area resolves to the wrong Area name.

Either I've done something funky to make it do this or I'm not quite understanding how this Action method is meant to work.

UPDATE 1

I can make this work by doing the following:

return helper.Action("add", "product",new {area = "storemanagement"});

which changes my question slightly.

How come the MVC routing doesn't disambiguate the controllers with the same name and resolve to the one with the action method specified?

Origional post

Hey everyone,

I've created a helper method on the UrlHelper class and am having a small problem with one of the routes.

Here's the code for the helper method in question:

public static string AddProduct(this UrlHelper helper)
        {
            return helper.Action("add", "product");
        }

I basically have two controllers named "product" which are in different areas of the site. One of them in used for browsing of products and the other for management of products. Only one of the product controllers contains an action method "Add".

When I output the value of AddProduct

<%: Url.AddProduct() %>

The area name is resolved to the current area I'm browsing and not the correct area for the product controller containing the Add action method.

Is there something I need to set up in the routes? I'm not exactly sure how the routing works with UrlHelper.Action so I dunno if it's possible to do what I'm trying.

Cheers for any help.

2
try adding the controller and area name as additional arguments using this method sig UrlHelper.Action Method (String, String, Object)Ahmad
Thanks Ahmad. That actually does work so i'll add it as an update to my question. I'm still interested in why the routing system doesn't disambiguate the two controller and instead just seems to give up and assume the use of the current area.Jamie Dixon
to answer your question, one needs to dig deep in the internals of the MVC framework to find out why this happens. In any event, from what I have seen only the controller and action routevalues have special treatment i.e. area are kinda inferred/assumed based on where you calling the helper from. Thus the need to specify the area parameter.Ahmad

2 Answers

2
votes

Just to put the answer in the answer section for clarity...

You'll need to add Area to the RouteValues anytime you use UrlHelper.Action to generate a link path.

If you're in the Controller, you can stub a UrlHelper like this:

var httpContext = new HttpContextWrapper(System.Web.HttpContext.Current);
var requestContext = new RequestContext(httpContext, new RouteData());
var urlHelper = new UrlHelper(requestContext);

You can always get the current area like this:

HttpContext.Current.Request.RequestContext.RouteData.DataTokens["area"]

Finally, when using UrlHelper pass the area in the RouteValues object like this:

var url = urlHelper.Action("Index", "Home", new { area = "Pages" }));
1
votes

That is the default behavior of ASP.NET Routing.

When in an "Area" the action (and view) are searched for by controller and action name in default locations. The view is presumed to be ActionName unless stated otherwise in the action e.g. return PartialView("_MyPartialView.cshtml", ViewModel)

The default locations are these: {Controller} = controller name, {Area} = area name

Controller:

"Areas/{Area}/{Controller}/"
"Controllers/{Controller}"

Views:

"Areas/{Area}/Views/{Controller}/" 
"Areas/{Area}/Views/Shared/"
"Views/Shared"

If you don't give the Area in the route values it will never search outside these locations. The Area is the same as the location where you are calling your helper. If you are in the root level (not in an area) it will be limited to

"Controllers/{Controller}" // controller
"Views/{Controller}/" // views
"Views/Shared" // views

The problem was that when you where in one Area1 it searched for "Areas/Area1/{Controller}/" and when you were in Area2 it searched "Areas/Area2/{Controller}/" (And both searched the Controllers/Product and Controllers/Shared). It was able to find it when you where in the right area because it fit the default search location, but not while in the other area because the controller was only in the one physical area.

Adding the Area as you did, tells it to search in a predefined Area so it will go directly there. "Areas/Storemanagement/Views/product/" and search for the view defined in the Add Action.

There is little point in having an empty helper method returning a Url.Action method, but perhaps that was just for demonstration.

(I just noticed the question is quite old but I guess this can be here for future reference)