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:

Testing ASP.Net MVC Controllers with FluentMVCTesting

FluentMVCTesting facilitates the writing of tests against MVC controllers in a fluent way.

FluentMVCTesting is available via NuGet: Install-Package TestStack.FluentMVCTesting

There’s a number of things that FluentMVCTesting can help to test such as:

  • A controller action returns the correct view
  • A controller action returns the correct HTTP status
  • A controller action returns an empty result
  • A controller action returns a view if there are model errors
  • A controller action returns a view with the correct model data
  • A controller action should redirect to a Url / Route / Action

It’s also not tied to a specific testing framework, so it can be used with NUnit, xUnit.net, MSTest, etc.

Examples

var sut = new ExampleController();

sut.WithCallTo(x => x.Show()).ShouldRenderView("Orders");

The preceding test code is testing the ExampleController. It is testing that when the Show() action is called then the Orders view is rendered.

The following code checks that an expected HTTP status is returned.

var sut = new ExampleController();

sut.WithCallTo(x => x.MakeAnError())
    .ShouldGiveHttpStatus(HttpStatusCode.InternalServerError);

 

To see more of what FluentMVCTesting can do check out my Building the Right Thing in .NET with TestStack Pluralsight course, check out the documentation, or check it out on GitHub.

SHARE:

Introducing JS Name-O-Tron – Find a Name for Your JavaScript Library

As a Microsoft MVP I get free Azure credits to use every month, so I thought I’d better start making use of them :)

screenshot of JS Name-O-Tron application on Azure

JS Name-O-Tron is the first web site I’ve deployed to Azure and I’m pleased to say it was crazy-easy :)

It generates a random word and adds “.js” to it – you can then check if there’s an existing library with that name (GitHub, CodePlex, and NuGet).

I used Visual Studio 2013 to create a new ASP.Net application and chose MVC (v5) which resulted in a Bootstrapped site with a default Home controller and views.

More...

SHARE: