An unexpected exception which resulted in a gentle reminder of how Linq works...


Tonight I spent some time continuing my port of a Mobile Application I developed a few years ago over to UWP. As it was built in Xamarin.Forms , which also supports UWP, so far its been a fairly straight forward process. During testing tonight however I kept getting an uncaught exception every time I tapped a list item.
Specified argument was out of the range of valid values - Source Xamarin.Forms.Core

Nasty Exception
Now first I double checked the code and found UWP, iOS and Android all run the same code and nothing had changed, so I hadn't accidentally broken anything. What I did note was the pages that didn't have grouped list items worked fine, where as grouped lists always crashed. This lead me to take a look at my ItemSource code for the listview.

My ListView ItemSource
Now those observant might notice in the pic I'm inspecting the listingNavigationItems, and like I might have realised the potential issue. Dependent on whether grouping is enabled I either return the first of my grouped sections OR I return all of the grouped sections, BUT I'm using Linq to perform this grouping by projecting my objects into a new type as I go. You can see in the pic that my collection isn't a list or an IEnumerable, its of type System.Linq.Enumerable.WhereSelectListIterator. When I return this my Linq operation hasn't performed yet, Linq by design uses lazy evaluation which means it doesn't create the final result until something enumerates the collection, it also doesn't have to do this all in one go, it can evaluate as each item is enumerated. This is great when you continue chaining Linq operators in different methods etc as it means it performs as optimally as it can (it's well worth reading an old MSDN article by Eric Wright which goes through it in detail) but it does have some side effects which if you aren't careful can come back to bite you.

This is a perfect example of one, I'm assuming a fair bit here but I imagine there's a difference between how the various Xamarin.Form ListView implementations work. The iOS and Android implementations are quite happily lazily evaluating my Linq and manage the listview index's, where as the UWP implementation expects to have a materialised collection with all section indexes known,. Maybe all item indexes as well I'm not 100% sure.

By tweaking my code to materialise the WhereSelectListIterator before returning it, by calling ToList(), my UWP app is now happy again.


So there it is, it's sometimes easy to forget what Linq is actually doing and what it can mean for our applications, most of the time everything works as expected but its always worth ensuring you understand the basic concepts of it when things do go wrong, it can be the difference in spending 30 minutes looking at a bug and much longer...

Comments

Popular posts from this blog

Can you use BuildRoot with Windows Subsystem for Linux......

DotNet CLI , private NuGet feeds and Linux...

WebUSB - An unexpected update...