Promoting SoC Through Application Layering

Creating a layered design in an application is a fundamental element of modern software architecture. The goal is to promote the Separation of Concerns (SoC) design principle. Separation of Concerns The ideas behind SoC date back to Dijkstra's 1974 paper "On the role of scientific thought" . In computer science, separation of concerns (sometimes abbreviated as SoC) is a design principle for separating a computer program into distinct sections. Each section addresses a separate concern, a set of information that affects the code of a computer program. A concern can be as general as "the details of the hardware for an application", or as specific as "the name of which class to instantiate". A program that embodies SoC well is called a modular program. Modularity, and hence separation of concerns, is achieved by encapsulating information inside a section of code that has a well-defined interface. - Wikipedia SoC is a broad design principal th...

Using Reflection to Retrieve a Property Attribute in .NET: Part 2

The previous post showed how to get a property Attribute using Reflection. Let's examine a real world scenario where this is useful. If you missed the previous post, you can view it here. This post will be building off of that example.

Browser Screenshot of Demo Application

Take the following class:

public class Reward
{
	[Required]
	public int Id { get; set; }

	[StringLength(32, MinimumLength = 1)]
	[Display(Name = "Reward Name")]
	public string Name { get; set; } = string.Empty;

	[StringLength(128, MinimumLength = 1)]
	[Display(Name = "Reward Description")]
	public string Description { get; set; } = string.Empty;
}

To concisely access the DisplayAttribute in code, let's expand the AttributeUtility class that was created in the previous post. We can add new methods to fetch the DisplayAttribute.Name value for a property in a class.

/// <summary>
/// based on the passed expression, lookup the PropertyName and then lookup the DisplayAttribute.Name value
/// </summary>
/// <typeparam name="TClass"></typeparam>
/// <param name="expression"></param>
/// <returns></returns>
public static string GetDisplayName<TClass>(Expression<Func<TClass, object>> expression)
{
   // get the property name using the expression
   var propertyName = GetPropertyNameFromExpression(expression);

   // lookup the DisplayAttribute.Name value
   return GetDisplayName<TClass>(propertyName);
}

/// <summary>
/// based on the passed property name, lookup the DisplayAttribute.Name value
/// </summary>
/// <typeparam name="TClass"></typeparam>
/// <param name="propertyName"></param>
/// <returns></returns>
public static string GetDisplayName<TClass>(string propertyName)
{
   // get the Display attribute for the property name
   var attr = GetAttribute<TClass, DisplayAttribute>(propertyName);

   if (attr != null)
   {
	   return attr.Name ?? propertyName;
   }

   // by default, just return the propertyName
   return propertyName;
}

This can be useful in a variety of scenarios. Below is one example of how this can be helpful in a Razor Component:

<table class="table">
    <thead>
        <tr>
            <th>@(AttributeUtility.GetDisplayName<Reward>(f => f.Name))</th>
            <th>@(AttributeUtility.GetDisplayName<Reward>(f => f.Description))</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var reward in rewards)
        {
            <tr>
                <td>@reward.Name</td>
                <td>@reward.Description</td>
            </tr>
        }
    </tbody>
</table>

The above code dynamically looks up the DisplayAttribute.Name value for a property and uses that to populate the table's column header. Additionally, there are no hardcoded strings and many issues will be discovered at compile-time rather than at runtime.

This example was created using Visual Studio 2022 running a Blazor Web App project.

Source code: https://github.com/jharrell-bits/ReflectionAttributePart2Demo