Non-database data manipulation using LINQ and extensions

I’ve been using LINQ more and more over the last year or so, not just as a tool for accessing database data via Entity Framework, but also for manipulating non-database data and with much less coding than before. I ran into a good example of this today as I needed to remove some items from a collection.

In short, I needed to remove keys of a certain prefix from the ModelState of an ASP.NET MVC view. In the past, I might have done something like this:

string[] keys = ModelState.Keys.ToArray();
foreach(string key in keys)
{
    if (key.StartsWith("PrefixToBeMatched"))
    {
        ModelState.Remove(key);
    }
}

We basically iterate through the keys, find the ones that match our prefix, and then remove those that match our prefix. This isn’t exactly complicated code, but should serve as a good, simple example of how you can use LINQ to accomplish things in new ways and with less code.

Using LINQ, we can shorten the code to the following:

ModelState.Keys.Where(key => key.StartsWith("PrefixToBeMatched")).Select(key => ModelState.Remove(key));

Now we have just one line of code, which basically says “where a key from ModelState.Keys matches the prefix (the Where method), go ahead and remove it (the Select method).

Great, right? Well, it doesn’t work. The code executes fine, but nothing happens. Why?

With LINQ, you’re really building an expression (the “Where” method in this case), which in turn can have cascading expressions (the “Select” method in this case). However, these expressions aren’t actually executed until you request the results by assigning them to an object. Huh?

Most of the time, you’re using LINQ to fetch some data and then assign that data to something else, either for additional processing or for rendering to a UI. In those cases. you typically will convert the LINQ expression to another type via one of the many “To…” extension methods (ToList, ToDictionary, ToArray, etc.). It’s in these methods where the expression is actually executed and the results are assigned. This way, you can build a complex, chained set of expressions with no performance hit until the very moment that the results are assigned to something.

Going back to the earlier example, though, we see that we never assigned the results to anything. This is because this LINQ expression isn’t about retrieving data, but rather changing it (via the call to the ModelState.Remove method). So, if all we have to do is call one of the “To…” extension methods to retrieve the results, we should be in good shape…

ModelState.Keys.Where(key => key.StartsWith("PrefixToBeMatched")).Select(key => ModelState.Remove(key)).ToArray();

All we did was chain a call to ToArray at the very end of previous code. Since we didn’t really care about the results (but rather the action carried out), we didn’t assign the results to anything.

Now everything should be good, right? Well, not quite. Running this code will generate an error. This is because there’s one other subtle issue here that’s very specific to this scenario: we can’t remove items from a list that we’re currently iterating through. This is not a LINQ issue though. In fact, in the original code where we weren’t using LINQ, we would have faced the same problem if we had iterated through ModelState.Keys rather than the keys string array to which we copied the list of keys. So, it sounds like we need to do the same thing in our LINQ expression…

ModelState.Keys.Where(key => key.StartsWith("PrefixToBeMatched")).ToArray().Select(key => ModelState.Remove(key)).ToArray();

See where we inserted a call to ToArray between the Where and Select method calls? That does the trick!

We could stop here and be done. However, there’s something about having to assign the results of the Remove call to an object in order to get the expression to execute that just doesn’t feel right. One solution we could implement is to write an extension method called Execute that we could use in place of the Select method and then have that method carry out executing the expression for us. This also makes intuitive sense and also makes the code read better. The extension method looks like this:

public static void Execute<T>(this IEnumerable<T> list, Action<T> action)
{
    foreach(T item in list)
    {
        action(item);
    }
}

By executing the action (calling action(item)), we cause the expression to execute the same way that we did by calling ToArray earlier. With this extension now available, our key manipulation code can be revised to look like this:

ModelState.Keys.Where(key => key.StartsWith("PrefixToBeMatched")).ToArray().Execute(key => ModelState.Remove(key));

With this example, we demonstrated how to use LINQ and extensions to reduce and simplify non-database data manipulation. Plus, we created a new extension method that can be re-used for other similar data manipulation scenarios.

Enjoy!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s