46

I recently discovered Areas in ASP.NET MVC 4, which I have successfully implemented, but I'm running into troubles with the @Html.ActionLink helper in my Razor views. The URL this helper generates always seems to be relative to the URL of the current page.

I have my web site configured in IIS 7 (Win7) as an Application virtual directory at /Foo.

I have two Areas registered: Admin and Blogs. I have the default code in the AdminAreaRegistration.cs and BlogsAreaRegistration.cs files. I added namespaces = new[] { "MvcProject.Controllers" } to the defaults of the default RouteConfig:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new {
                controller = "Home", action = "Index", id = UrlParameter.Optional,
                namespaces = new[] { "MvcProject.Controllers" }
            }
        );
    }
}

When I go to the home page for my site: http://localhost/Foo, it correctly loads the "home" page for my site. At this point, all the action links have their correct URLs.

Sample code from MvcProject/Views/Shared/_Layout.cshtml

<h2>Main Navigation</h2>
<ul>
    <li>@Html.ActionLink("Home", "Index", "Home")</li>
    <li>@Html.ActionLink("Blogs", "Index", "Blogs/Home")</li>
    <li>@Html.ActionLink("Admin", "Index", "Admin/Home")</li>
</ul>

This is correctly rendering the HTML as:

<h2>Main Navigation</h2>
<ul>
    <li><a href="/Foo">Home</a></li>
    <li><a href="/Foo/Blogs/Home"></li>
    <li><a href="/Foo/Admin/Home"></li>
</ul>

When I navigate in the browser to "Blogs" for instance, this URL correctly loads in the browser: /Foo/Blogs/Home.

Now the links in my main navigation change their URLs to:

<h2>Main Navigation</h2>
<ul>
    <li><a href="/Foo/Blogs/Home">Home</a></li>
    <li><a href="/Foo/Blogs/Blogs/Home"></li>
    <li><a href="/Foo/Blogs/Admin/Home"></li>
</ul>

Notice that "Blogs/" is appended to the IIS virtual directory name, so that /Foo/Blogs/Home is now /Foo/Blogs/Blogs/Home.

The controllers and views are rendering fine, it's just the calls to @Html.ActionLink in my MvcProject/Views/Shared/_Layout.cshtml view are not working as I expected.

It feels like I'm missing something trivial, but no amount of searching has come up with an answer. Every blog post and tutorial I've found for implementing Areas in ASP.NET MVC4 makes no mention of changes in how @Html.ActionLink behaves.

Greg Burghardt
  • 17,900
  • 9
  • 49
  • 92

4 Answers4

76

I hate answering my own question, but @Matt Bodily put me on the right track.

The @Html.Action method actually invokes a controller and renders the view, so that wouldn't work to create a snippet of HTML in my case, as this was causing a recursive function call resulting in a StackOverflowException. The @Url.Action(action, controller, { area = "abc" }) does indeed return the URL, but I finally discovered an overload of Html.ActionLink that provided a better solution for my case:

@Html.ActionLink("Admin", "Index", "Home", new { area = "Admin" }, null)

Note: , null is significant in this case, to match the right signature.

Documentation: @Html.ActionLink (LinkExtensions.ActionLink)

Documentation for this particular overload:

LinkExtensions.ActionLink(Controller, Action, Text, RouteArgs, HtmlAttributes)

It's been difficult to find documentation for these helpers. I tend to search for "Html.ActionLink" when I probably should have searched for "LinkExtensions.ActionLink", if that helps anyone in the future.

Still marking Matt's response as the answer.

Edit: Found yet another HTML helper to solve this:

@Html.RouteLink("Admin", new { action = "Index", controller = "Home", area = "Admin" })
starlocke
  • 3,407
  • 2
  • 25
  • 38
Greg Burghardt
  • 17,900
  • 9
  • 49
  • 92
  • 30
    Just FYI, answering your own question is still a great thing! It allows people like me to show up and find the answer to a problem/question I might have had. Thanks! – muttley91 Jan 18 '15 at 16:26
  • This answer helped me out with my problem. I was getting a error that said `The controller for path '/' was not found or does not implement IController.` It was because I deleted my HomeController.cs file and I didn't understand what depended on it. – user2023861 Dec 01 '17 at 21:17
  • Solution proved to be most useful. I was trying without including "null". – shiju87 Feb 13 '18 at 07:29
  • @Colin: Visual Studio 2017 is nice, because it tells you more explicitly that a method is an extension method. Older versions of VS weren't quite as usable in that regard. I _still_ struggle with my Google-fu when it comes to these Razor template extension methods. – Greg Burghardt Jul 30 '18 at 18:52
15

How I redirect to an area is add it as a parameter

@Html.Action("Action", "Controller", new { area = "AreaName" })

for the href portion of a link I use

@Url.Action("Action", "Controller", new { area = "AreaName" })
Matt Bodily
  • 6,403
  • 4
  • 29
  • 48
  • So, funny coincidence. As soon as I added that to my view, I got a StackOverflowException :) Not really sure why... Time to dig into the Windows event logs. – Greg Burghardt Mar 14 '14 at 14:31
  • 2
    After doing a little reading, it appears as though `@Html.Action(...)` actually invokes and renders the action. In my case, I'm just trying to spit out a URL. I think I'm getting a stack overflow because the main layout file renders the Blogs/Home action, which pulls in the layout file, which renders the Blogs/Home action, which pulls in the main layout file, which ... and on we go to infinity. – Greg Burghardt Mar 14 '14 at 14:37
  • for the href portion of a link or for a url that I use in jquery I use url.action instead. see my change above – Matt Bodily Mar 14 '14 at 14:45
2

Just to add up my bit:
Remember, you're gonna need to have at least 2 areas in your MVC application to get the routeValues: { area="" } working; otherwise the area value will be used as a query-string parameter and you link will look like this: /?area=

If you don't have at least 2 areas, you can fix this behavior by:
1. editing the default route in RouteConfig.cs like this:

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { area = "", controller = "Home", action = "Index", id = UrlParameter.Optional }
);

OR
2. Adding a dummy area to your MVC project.

Achilles
  • 1,554
  • 1
  • 28
  • 36
  • In my case we started with the original MVC project structure and then later added an area. I had to make the above change in order for the logo url to the default home page work across all controllers. – BrandonG Mar 18 '15 at 20:28
2

Below are some of the way by which you can create a link button in MVC.

@Html.ActionLink("Admin", "Index", "Home", new { area = "Admin" }, null)  
@Html.RouteLink("Admin", new { action = "Index", controller = "Home", area = "Admin" })  
@Html.Action("Action", "Controller", new { area = "AreaName" })  
@Url.Action("Action", "Controller", new { area = "AreaName" })  
<a class="ui-btn" data-val="abc" href="/Home/Edit/ANTON">Edit</a>  
<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#CustomerList" href="/Home/Germany">Customer from Germany</a>  
<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#CustomerList" href="/Home/Mexico">Customer from Mexico</a> 

Hope this will help you.

Suraj Kumar
  • 5,547
  • 8
  • 20
  • 42
  • 1
    Just be careful with `Html.Action(...)`. As I found out it doesn't create a link. It renders a controller. Otherwise a good summary of the methods. Thanks. – Greg Burghardt Nov 05 '18 at 12:21