Razor is a sharp user interface language, but it can also cut into our sensibilities and aesthetic code choices. ASP.NET ships with many HTML helpers to help reduce the amount of code we write to ship user interfaces, and for the most part, they work pretty well. With the latest version of ASP.NET, there are techniques we can use to reduce the noise in our views.

In this post, we’ll see how we can use the newly introduced DisplayNameForInnerType to clean up our views.

DisplayAttribute

The DisplayAttribute ships as part of the System.ComponentModel.DataAnnotations library and allows us to decorate properties with user-facing names that may differ from the original property name.

public class Person
{
    [Display(Name = "Name")]
    public string FullName { get; set; }
}

In this example, we can see that our Person class has a property name of FullName but has a display name of Name. HTML helpers can use the attributes to build labels and display elements to our needs.

Assuming our view’s model is a Person, we can write the following in the view.

<label>@Html.DisplayNameFor(m => m.FullName);</label>

We will get the following result.

<label>Name</label>

That’s great! We can also use the DisplayAttribute for other metadata values:

  • ShortName: used as a grid column label.
  • Description: used as a tooltip or description.
  • Prompt: used as a prompt or watermark.
  • GroupName: a group name for grouping fields on the UI.
  • Order: The order in which we display the field.

All of these properties can be the literal value or a resource key string.

The Issue With Enumerables

The example used above is the best-case scenario for our view models. Our model is flat with no arrays, but it becomes a little bit more complicated when we start to introduce collections. Let’s take a look at our IndexModel that powers our Razor page.

public class IndexModel : PageModel
{
    private readonly ILogger<IndexModel> _logger;

    public List<Person> People { get; set; }
        = new List<Person>();

    public IndexModel(ILogger<IndexModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
    }
}

As we can see, the page model has a collection of Person objects. To get the display name for our FullName we may be tempted to write this in our Razor view.

<td>@Html.DisplayNameFor(m => m.People.First().FullName)</td>

The call will work but uses LINQ to traverse the object graph in a way that is repetitive and noisy. Let’s look at a better alternative. Newer versions of ASP.NET have introduced a DisplayNameForInnerType, which allows us to type the expression strongly without being beholden to the structure of our model.

<td>@Html.DisplayNameForInnerType((Person p) => p.FullName)</td>

It is crucial that our expression has a declared parameter type.

What’s happening inside of the extension method?

public string DisplayNameForInnerType<TModelItem, TResult>(
    Expression<Func<TModelItem, TResult>> expression
)
{
    if (expression == null)
    {
    throw new ArgumentNullException(nameof(expression));
    }

    var modelExpression = _modelExpressionProvider
    .CreateModelExpression(
        new ViewDataDictionary<TModelItem>(ViewData, model: null),
        expression
    );

    return GenerateDisplayName(
        modelExpression.ModelExplorer, 
        modelExpression.Name
    );
}

ASP.NET uses an internal ModelExpressionProvider to create a new ViewDataDictionary with the specified model type and then uses the same code already used by other helpers.

Conclusion

So there we have it. We can now generate display names without the need for complicated LINQ expressions. What it means for us are cleaner views and, hopefully, less code. It’s a win/win situation for everyone.

I hope you enjoyed this post, and please leave a comment below.