You sneaky deferred execution!

As I’ve mentioned we have a series of Extension methods that help with some trivial, but isolated tasks. Here is an example.

public static IEnumerable<SelectListItem> CreatePaymentTypeDropDownListItems(
                                          this IQueryable<PaymentType> @this)
{
    return  @this
            .OrderBy(x => x.Description)
            .Select(x => new SelectListItem
                             {
                                 Value = x.Code,
                                 Text = x.Description,
                             })
            .MarkAsSelected(x=>x.Text == CreditCard);
}

Simple enough right? The MarkAsSelected is another extension method we have to help out. It’s not all that exciting, but here it is.

public static IEnumerable
 MarkAsSelected(this IEnumerable
 @this, Func
 where)
{
    var items = @this.Where(where);
    foreach (var item in items)
    {
        item.Selected = true;
    }
    return @this;

}

Simple enough right? But the credit card payment type was never getting defaulted with the select value. We have unit tests around MarkAsSelected and it works! What’s the problem? Let’s try something really quick.

public static IEnumerable<SelectListItem> CreatePaymentTypeDropDownListItems(
                                          this IQueryable<PaymentType> @this)
{
    return  @this
            .OrderBy(x => x.Description)
            .Select(x => new SelectListItem
                             {
                                 Value = x.Code,
                                 Text = x.Description,
                             })
            .ToArray()
            .MarkAsSelected(x=>x.Text == CreditCard);
}

That works! What’s up? Oh you sneaky deferred execution. I bet it’s you! I’m not an expert, but here is my best guess. When the MarkAsSelected does its foreach, it asks the @this to create an enumerator. It does, but since it is from an IQueryable<T>, it now executes all that deferred execution in the Select and OrderBy. Fair enough. The enumerator spits out all the SelectListItems we want/need.

We pass on the enumerable incase we want to do something after it, and actually we do. Down the road we want to enumerate again. The Select and OrderBy kick in again and we get all brand new SelectListItems.

Throwing that ToArray in there gets us out of operating on the IQueryable<T>, does the Select and OrderBy and lets us operate on a straight list. Makes sense, but not obviously apparent…at least to me.

But you know what? After all that, I think I just prefer this.

public static IEnumerable<SelectListItem> CreatePaymentTypeDropDownListItems(
                                          this IQueryable<PaymentType> @this)
{
    return  @this
            .OrderBy(x => x.Description)
            .Select(x => new SelectListItem
                             {
                                 Value = x.Code,
                                 Text = x.Description,
                                 Selected = (x.Description == CreditCard)
                             });
}

No need to overcomplicate the problem right?

Leave a Reply