Write a custom .NET attribute to mark searchable columns of a HTML table and match them with their equivalent DB columns

Consider you have to implement the following requirement:

Here a simple mockup of our UI:

Balsamiq mockup for article about .NET attribute and CallerMemberName

We are going to implement a .NET attribute to give the properties of our UI model class some metadata.

1
2
3
4
5
6
7
8
9
10
[AttributeUsage(AttributeTargets.Property)]
public class SearchableAttribute : Attribute
{
    public string DBColumnName { get; }

    public SearchableAttribute(string dbColumnName = null, [CallerMemberName] string uiColumnName = null)
    {
        DBColumnName = dbColumnName ?? uiColumnName;
    }
}

We restricted the use of the attribute to properties only. The Searchable attribute can get up to two arguments.

Now lets apply the attribute to our simple model class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Person
{
    [Searchable]
    public string Firstname { get; set; }

    [Searchable("Name")]
    public string Lastname { get; set; }

    public string Sex { get; set; }

    [Searchable]
    public int Age { get; set; }

    public string Country { get; set; }    
}

We made three out of five properties of our model class searchable and as you can see, the Lastname property has another name in the database. We define this name as the first argument of the attribute.

We are ready to write a helper function which gets the name of the UI column and based on reflection it finds the mapping to the equivalent DB column by using the Searchable attribute:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static string MapUIColumnNameToDBColumnName<T>(string uiColumnName)
{
    var modelProperties = typeof(T).GetProperties();

    try
    {
        var dbColumnName = modelProperties.Single(m => m.Name == uiColumnName)
            .GetCustomAttributes(typeof(SearchableAttribute), false).Cast<SearchableAttribute>()
            .SingleOrDefault()?.DBColumnName;

        return dbColumnName;
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
        throw;
    }
}

The MapUIColumnNameToDBColumnName works as follows:

To recap, we just saw two very helpful features we can use while working with C# and the .NET framework. The first was the CallerMemberName attribute and the second was the accessing of the custom attributes of the properties of a class by using reflection.

I hope you enjoyed this article. Write a comment if you have any thoughts.

comments powered by Disqus