Reputation: 1539
I have come across through a strange behaviour of Linq : with two linq expressions that might seem identical I have different results! If I loop once I get the same result, but above it finds nothing.
Here is the code:
Dictionary<String, String> mainDico = new Dictionary<String, String>();
mainDico.Add("key1", "value1");
mainDico.Add("key2", "value2");
List<Dictionary<String, String>> ls = new List<Dictionary<String, String>>();
Dictionary<String, String> fistDico = new Dictionary<String, String>();
fistDico.Add("key1", "value1");
fistDico.Add("key2", "value2");
Dictionary<String, String> secondDico = new Dictionary<String, String>();
secondDico.Add("key1", "other");
secondDico.Add("key2", "other");
ls.Add(fistDico);
ls.Add(secondDico);
IEnumerable<Dictionary<String, String>> failQuery = from p in ls
select p;
IEnumerable<Dictionary<String, String>> successfulQuery = from p in ls
select p;
String[] items = new String[] { "key1","key2" }; // with one element it works
foreach (var item in items)
{
String temp = mainDico[item];
failQuery = failQuery.Where(p => p[item] == temp);
successfulQuery = successfulQuery.Where(p => p[item] == mainDico[item]);
}
Console.WriteLine(successfulQuery.SingleOrDefault() != null);//True
Console.WriteLine(failQuery.SingleOrDefault() != null);//False
Upvotes: 4
Views: 112
Reputation: 203821
The problem is that you're closing over the loop variable.
The problematic section of code is right here:
foreach (var item in items)
{
String temp = mainDico[item];
failQuery = failQuery.Where(p => p[item] == temp);
successfulQuery = successfulQuery.Where(p => p[item] == mainDico[item]);
}
You're creating a lambda that closes over item
in the second case (and also the first case; you should really fix that), and you're not evaluating the query until after the end of the foreach loop; that means that item
will always be the last item in the foreach loop, not the current item. This can be easily resolved by creating a new local variable, which is what you do in the first case, which is why that works.
Here is a related link that discuss the matter in more detail. (You can find lots more by searching over "close over loop variable".
Note that this was changed in C# 5.0 since it's a frequent cause of confusion and bugs. (This is likely why certain people couldn't reproduce the problem.)
It's also worth noting that this has nothing to do with the dictionary. In your query item
is effectively always the last item in the foreach
, rather than the current, which is why it fails. Anything that you did with item
that relied on it being the current value wouldn't do what you wanted.
Upvotes: 5