LINQ Gotcha
After a long hiatus, I was trying to figure out something cool or at least interesting to blog about. I was having trouble figuring out what I wanted to write, but then one after another, I got a ton of ideas.
One thing I wanted to do for my current project was finding out if a specific ConnectionString was present in the <ConnectionStrings> setting of a .config file. This is a big oversimplification but it's close enough to explain. So I wrote the following:
var myVar = from cs in ConfigurationManager.ConnectionStrings
select cs;
foreach (var Mine in myVar)
{
Console.WriteLine(myVar);
}
I knew the second I tried to write a where clause that there was a problem b/c intellisense wasn't showing anything I expected. When I tried to display some of the properties in the foreach loop, nothing was visible. I knew something was wrong. So I tried to compile and got the following error:
Could not find an implementation of the query pattern for source type 'System.Configuration.ConnectionStringSettingsCollection'. 'Select' not found. Consider explicitly specifying the type of the range variable 'cs'.
Well, that was helpful b/c I knew at least the problem was with the range variable . I wasn't sure what type of collection the ConfigurationManager.ConnectionStrings property was so I decided to look it up just to be sure. (I also just wanted to make sure it was in fact some type of collection or another. I couldn't imagine that it wouldn't have been, but just wanted to make sure for purely superstitious reasons) Ok, so it's a ConnectionStringsSettingsCollection , nothing surprising there. Digging deeper I saw that ConnectionStringsSettingsCollection inherits from ConfigurationElementCollection . At that point, it all became crystal clear for I saw the following in the class definition:
public abstract class ConfigurationElementCollection : ConfigurationElement, ICollection,
IEnumerable
Do you see the problem? There's an IEnumerable but no IEnumerable<T> . You see, the collection implements the non-Generic IEnumerable but not the Generic IEnumerable. Hence, a explicit range variable is in order to make this work. I know I know, that's exactly what the error message recommended, but I didn't understand why at first and wanted to dig deeper into it. So bascially, here's what was needed to make it work. Simply use the Cast<T> extension method on the collection and well, that's it.
ConnectionStringSettingsCollection cfg = ConfigurationManager.ConnectionStrings;
var myvar = from cs in cfg.Cast<ConnectionStringSettings>()
select cs;
foreach (var mine in myvar)
{
Console.WriteLine(mine);
}
Of course, you could just cut out some of the bloat and address the collection directly
var myvar = from cs in ConfigurationManager.ConnectionStrings.Cast<ConnectionStringSettings>()
select cs;
foreach (var mine in myvar)
{
Console.WriteLine(mine);
}
Whenever Intellisense doesn't do what you're expecting it to, that's the first tipoff something is wrong in your query. Although my days of 2 day work weeks have come to an end temporarily, I'll try to find some time to blog the rest of what I was working on - there's so much you can do with LINQ that I often overlook. And this is the first time I've come across the explicit range variable issue but I'm guessing it'll come up again, particularly if you use LINQ regularly.