How to link to Controller Actions from Views in ASP.NET MVC Core

22 Mar 2021

Often you need to put links in your ASP.NET MVC Core Views that take users to other controllers in your web app – like navigating to a details page for a particular record in the table. Or when building a primary, secondary or tertiary navigation. Or logging users in or out. Heck, you need links everywhere!

A very obvious (but wrong!) way is to hardcode relative paths to your controller’s actions in the View, like <a href="/product/details/1">View product details</a>. It’s super easy, right? What can possibly go wrong? Actually, a lot of things. Just the tip of the iceberg:

As you can see, hardcoding is a big pain in the rear. But what can you do instead? Fear not, there’s a solution that addresses all the above problems and more!

Let’s look into automatic link generation, and how you can pimp it up to make it type-safe, ensuring compile-time checking, so no 404s ever happen when you refactor your code by renaming controllers or their methods.

There are at least three different ways to make links in ASP.NET MVC Core:

Unfortunately, as it’s often the case with Microsoft’s docs, it’s quite cryptic. Like how the hell are you supposed to know, based on the very scant documentation, which particular method you need to call?

Instead of trying to explain what all these parameters mean, let me just show you the most common use cases of this extension method.

This extension method is to be called from Views:

  <p>This link is generated in the View:</p>
    <ul>
        <li>@Html.ActionLink("Simple link to a 'Home' controller 'Index' action", "Index", "Home")</li>
    </ul>
  </p>

Anchor Tag Helper

While @Html.ActionLink() is a carry-over left in ASP.NET Core for migration from .NET, the brand-spanking new way of generating links is the Anchor Tag Helper. Sporting much improved readability, it lets you do this:

  <p>This link is generated in the View:</p>
    <ul>
        <li><a asp-controller="Home" asp-action="Index">Simple link to a 'Home' controller 'Index' action</a></li>
    </ul>
  </p>

Check out the documentation for the full list of supported attributes, these being the most important ones:

This example shows how to generate a link with parameters:

  <a asp-controller="Home" asp-action="Details"
     asp-route-id="100500"
     asp-route-name="John Doe">A link with parameters to 'Home' controller 'Details' action</a>

Url.Action Helper Method

Not all links need to be generated in Views, sometimes you need to build links in your Controllers too. UrlHelperExtensions class has Url.Action method that does just that. The documentation is similarly obscure, but now you know how to navigate it.

Some examples:

All the examples above suffer from a fundamental problem: if you rename a controller or action method, the links will break, but you’ll only going to find out about it when trying to use them. That’s clearly not good enough.

Let’s further improve the link generation by bringing in compile-time checking and retrieval of the data that we need. The following code uses Url.Action with a custom method GetControllerName and nameof operator to get names of Controller and Action:

Url.Action(nameof(HomeController.Details),
           Utils.GetControllerName<HomeController>(),
           new { name = "John Doe", id = 100500})

In the similar fashion, you can use that approach with both @Html.ActionLink and <a asp-controller="..."></a>:

<p>
  @Html.ActionLink("Safely generated link with parameters to 'Home' controller 'Details' action",
  nameof(HomeController.Details),
  Utils.GetControllerName<HomeController>(),
  new { name = "John Doe", id = 100500})
</p>
<a asp-controller="@(Utils.GetControllerName<HomeController>())"
   asp-action="@(nameof(HomeController.Details))"
   asp-route-id="100500"
   asp-route-name="John Doe">Safely generated link with parameters to 'Home' controller 'Details' action</a>>

And that’s a one-line implementation of GetControllerName utility function. The only reason we need it is to get rid of the Controller suffix at the end of the string.

public static string GetControllerName<T>() where T : Controller {
    return typeof(T).Name.Replace(nameof(Controller), string.Empty);
}

Conclusion

In this article, we looked at different ways of generating links in Views and Controllers in ASP.NET Core MVC. We also looked at how to make the link generation process resilient to changes in your application and avoid breaking the links.

Get complete, tested, and working source code for this article

Download fully tested and 100% working Visual Studio solution with the source code used in this article for FREE – just enter your name and email in the form below, and I’ll send you the download link right away.

You will also get access to all the source code for all existing and new articles on the site, and access to my mailing list, which receives handy timesaving tips on .NET Core programming.

Subscribe now and get helpful tips on developing .NET apps - never miss a new article.

You can unsubscribe at any time. I'll never share your email with anyone else.