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.