Type Safe SelectList Factory

I think that ASP.NET MVC is a huge step forward from ASP.NET Web Forms. Still, there are some parts of it that are disappointing. One is the SelectList constructor, that doesn’t use generics and has string parameters for field selection. To create a SelectList from an IEnumerable<> we would do something like this.

IEnumerable<Person> people = GetPeople();
SelectList selectList = new SelectList(people, "Id", "FullName");

This is again one of those places where the compiler won’t be able to help us. Writing member names as strings in the code is just horrible. To remedy this I wrote a factory method with those strings changed into lambdas.

IEnumerable<Person> = GetPeople();
SelectList selectList = 
  people.ToSelectList(people, p => p.Id, p => p.FullName);

ToSelectList Extension Method

The ToSelectList method extracts the member names from the lambdas and then calls the normal SelectList constructor. Thanks to the ExpressionHelper.GetExpressionText helper method the code is short.

public static SelectList ToSelectList<TSource, TValue, TText>(
    this IEnumerable<TSource> source,
    Expression<Func<TSource, TValue>> dataValueField, 
    Expression<Func<TSource, TText>> dataTextField)
{
    string dataName = ExpressionHelper.GetExpressionText(dataValueField);
    string textName = ExpressionHelper.GetExpressionText(dataTextField);
    return new SelectList(source, dataName, textName);
}

ToSelectList is a generic extension method that takes three parameters.

  1. source is the IEnumerable<TSoudce> that contains the elements.
  2. dataValueField is a lambda expression selecting the member that is the value field.
  3. dataTextField is a lambda expression selecting the member that is the text field.

The ExpressionHelper.GetExpressionText from System.Web.Mvc extracts the field name from the lambda expression and then sends the retrieved string into the SelectList constructor. No more string constants referring to member names. Any typos or missed updates will now be caught during compilation.

4 comments

  1. Members as strings in code are not evil by itself, the problem is missing tooling support. If compile time errors, intellisense, refactoring, find all members etc were in place it would be no problem at all.

  2. var peopleOptions = GetPeople().ToDictionary(p => p.Id, p => p.Name);
    var selectList = new SelectList(peopleOptions, "Key", "Value");
    1. It’s a somewhat less risky way than using the bare SelectList constructor. The Key and Value fields will hardly change names, while there is a larger risk that the Person fields changes.
      My concerns are more about going over a Dictionary. One consideration is the performance cost of creating a dictionary. Another is the sorting. Isn’t there a risk that any order served by GetPeople will be lost when going over the dictionary?

      1. About performance, how many object does the ExpressionHelper.GetExpressionText() method create? I bet more than one.

        About sorting, ToDictionary() creates a Dictionary which does not change the order of items.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.