Using the C# 6.0 nameof Operator in ASP.NET MVC Razor Views

Traditionally to reference an action and/or a controller in a Razor view the action/controller name is represented as a string as shown in the following code:

<ul class="nav navbar-nav">
    <li>@Html.ActionLink("Home", "Index", "Home")</li>
    <li>@Html.ActionLink("About", "About", "Home")</li>
    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>

This means that some brittleness can be introduced into the application, for example if the name of the controller or action is changed these strings can become outdated and cause a runtime error.

The nameof operator introduced in C# 6.0 gets the (non fully qualified) name of the type passed to it. So for example nameof(HomeController) will return the string “HomeController”. The nameof operator can also be used with type members, for example nameof(HomeController.Index) will return the string “Index”.

The following code shows the use of nameof to get the action name programmatically without needing a magic string:

<ul class="nav navbar-nav">
    <li>@Html.ActionLink("Home", nameof(HomeController.Index), "Home")</li>
    <li>@Html.ActionLink("About", nameof(HomeController.About), "Home")</li>
    <li>@Html.ActionLink("Contact", nameof(HomeController.Contact), "Home")</li>
</ul>

Now if the name of the HomeController.Index method is renamed, for example to HomeController.Index2, there will be a build time error: “'HomeController' does not contain a definition for 'Index'”.

The use of nameof can also be applied to the controller name:

<ul class="nav navbar-nav">
    <li>@Html.ActionLink("Home", nameof(HomeController.Index), nameof(HomeController) )</li>
    <li>@Html.ActionLink("About", nameof(HomeController.About), nameof(HomeController))</li>
    <li>@Html.ActionLink("Contact", nameof(HomeController.Contact), nameof(HomeController))</li>
</ul>

The preceding code however causes errors, the link URL produced is “http://localhost:26663/HomeController/About” rather than the correct “http://localhost:26663/Home/About” (note the extra Controller text).

One way to rectify this would be to remove the word “Controller” from the string produced by nameof:

<ul class="nav navbar-nav">
    <li>@Html.ActionLink("Home", nameof(HomeController.Index), nameof(HomeController).Replace("Controller", ""))</li>
    <li>@Html.ActionLink("About", nameof(HomeController.About), nameof(HomeController).Replace("Controller", ""))</li>
    <li>@Html.ActionLink("Contact", nameof(HomeController.Contact), nameof(HomeController).Replace("Controller", ""))</li>
</ul>

This technique introduces some code duplication, which could be addressed by creating a string extension method:

public static class ControllerExtensions
{
    public static string RemoveController(this string fullControllerClassName)
    {
        return fullControllerClassName.Replace("Controller", "");
    }
}

And then using this extension method:

<ul class="nav navbar-nav">
    <li>@Html.ActionLink("Home", nameof(HomeController.Index), nameof(HomeController).RemoveController())</li>
    <li>@Html.ActionLink("About", nameof(HomeController.About), nameof(HomeController).RemoveController())</li>
    <li>@Html.ActionLink("Contact", nameof(HomeController.Contact), nameof(HomeController).RemoveController()))</li>
</ul>

Alternatively a static method could be created:

public static class ControllerExtensions
{
    public static string ShortControllerName<T>() where T : Controller
    {
        return typeof(T).Name.Replace("Controller", "");
    }
}

And then called from the view:

<ul class="nav navbar-nav">
    <li>@Html.ActionLink("Home", nameof(HomeController.Index), ControllerExtensions.ShortControllerName<HomeController>())</li>
    <li>@Html.ActionLink("About", nameof(HomeController.About), ControllerExtensions.ShortControllerName<HomeController>())</li>
    <li>@Html.ActionLink("Contact", nameof(HomeController.Contact), ControllerExtensions.ShortControllerName<HomeController>())</li>
</ul>

This technique may not work in all situations, for example if an action method is using the [ActionName] attribute:

[ActionName("Contact2")]
public ActionResult Contact()
{    
    // ...
}
In the preceding example, nameof(HomeController.Contact) will return the string “Contact” and the URL “http://localhost:26663/Home/Contact” wheras the correct URL should be “http://localhost:26663/Home/Contact2” because of the [ActionName("Contact2")] attribute.

Note that to get access to nameof, the view needs to be compiled with C# 6.0 language features enabled.

SHARE:

Comments (8) -

  • Mike C

    5/14/2016 1:01:01 AM | Reply

    I suspect it may be possible to write an overloaded ActionLink extension that could allow you to write the following, and reflection and NameOf is used internally to insert the correct link:

    @Html.ActionLink("Contact", HomeController.Contact)

  • Stuart

    5/18/2016 10:19:05 PM | Reply

    Mike, I don't think you'll be able to use nameof, because it will only return the name of the design time symbol, so you couldn't pass in a func and get it's name.
    The next best thing would be to use expressions like here: http://stackoverflow.com/a/20791462
    You could also use the routing APIs directly to get the attribute based routing working.

  • Jason

    5/28/2016 9:03:53 AM | Reply

    Thanks Michael

  • Chris Marisic

    6/16/2016 10:45:23 PM | Reply

    The nameof operator has definitely become my favorite C# 6 feature, i use it all over the place. It let's me do such wonderful things in loose typing but still strict adherence to my class definitions.

  • Chris Marisic

    6/16/2016 10:46:25 PM | Reply

    Regarding T4MVC, i used that project once. It was terrible in actual usage. You always had to remember to run the compiler to build the routes, routes would get checked in with mismatched compiled code into source control.

    I would never in my life ever choose to use T4MVC a 2nd time.

  • omid nasri

    11/29/2016 7:28:34 PM | Reply

    Hi, Yes if an action method is using the [ActionName] attribute that current post can't help, but we can create new extension method to get  ActionName and replace else return the current action name if not found.

  • rosdi

    6/1/2017 7:57:55 PM | Reply

    The vanilla nameof(HomeController).Replace("Controller", "") is actually more readable to me, also more flexible.. imagine if you have Controller named 'MainControllerController', .... you can then just use 'nameof(MainControllerController).Replace("nController", "n")' and it still work... (stupid yes... but you get the point)..

Pingbacks and trackbacks (5)+

Add comment

Loading