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.
If you want to fill in the gaps in your C# knowledge be sure to check out my C# Tips and Traps training course from Pluralsight – get started with a free trial.
SHARE: