System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary
c#, idictionary, linq-to-sql, linq-to-xml
Solution
The problem is with comparisons between the keys you are storing in the `Dictionary` and the keys you are trying to look up.
When you add something to a `Dictionary` or access the indexer of a `Dictionary` it uses the `GetHashCode()` method to get a hash value of the key. The hashcode for a `Tuple` is unique to that instance of the `Tuple`. This means that unless you are passing in the exact same instance of the `Tuple` class into the indexer, it will not find the previously stored value. Your usage of `mergedDict[Tuple.Create(...` creates a brand new Tuple with a different hash code than is stored in the `Dictionary`.
I would recommend creating your own class to use as the key and implementing `GetHashCode()` and the Equality methods on that class. That way the Dictionary will be able to find what you previously stored there.
More: The reason this is confusing to a lot of people is that for something like `String` or `Int32`, `String.GetHashCode()` will return the same hash code for two different instances that have the same value. A more specialized class such as `Tuple` doesn't always work the same. The implementor of `Tuple` could have gotten the hash code of each input to the `Tuple` and added them together (or something), but running Tuple through a decompiler you can see that this is not the case.
Problem
I receive the above error message when performing a unit test on a method. I know where the problem is at, I just don't know why it's not present in the dictionary. Here is the dictionary: ``` var nmDict = xelem.Descendants(plantNS + "Month").ToDictionary( k => new Tuple<int, int, string>(int.Parse(k.Ancestors(plantNS + "Year").First().Attribute("Year").Value), Int32.Parse(k.Attribute("Month1").Value), k.Ancestors(plantNS + "Report").First().Attribute("Location").Value.ToString()), v => { var detail = v.Descendants(plantNS + "Details").First(); return new HoursContainer { BaseHours = detail.Attribute("BaseHours").Value, OvertimeHours = detail.Attribute("OvertimeHours").Value, TotalHours = float.Parse(detail.Attribute("BaseHours").Value) + float.Parse(detail.Attribute("OvertimeHours").Value) }; }); var mergedDict = new Dictionary<Tuple<int, int, string>, HoursContainer>(); foreach (var item in nmDict) { mergedDict.Add(Tuple.Create(item.Key.Item1, item.Key.Item2, "NM"), item.Value); } var thDict = xelem.Descendants(plantNS + "Month").ToDictionary( k => new Tuple<int, int, string>(int.Parse(k.Ancestors(plantNS + "Year").First().Attribute("Year").Value), Int32.Parse(k.Attribute("Month1").Value), k.Ancestors(plantNS + "Report").First().Attribute("Location").Value.ToString()), v => { var detail = v.Descendants(plantNS + "Details").First(); return new HoursContainer { BaseHours = detail.Attribute("BaseHours").Value, OvertimeHours = detail.Attribute("OvertimeHours").Value, TotalHours = float.Parse(detail.Attribute("BaseHours").Value) + float.Parse(detail.Attribute("OvertimeHours").Value) }; }); foreach (var item in thDict) { mergedDict.Add(Tuple.Create(item.Key.Item1, item.Key.Item2, "TH"), item.Value); } return mergedDict; ``` and here is the method that is being tested: ``` protected IList<DataResults> QueryData(HarvestTargetTimeRangeUTC ranges, IDictionary<Tuple<int, int, string>, HoursContainer> mergedDict) { var startDate = new DateTime(ranges.StartTimeUTC.Year, ranges.StartTimeUTC.Month, 1); var endDate = new DateTime(ranges.EndTimeUTC.Year, ranges.EndTimeUTC.Month, 1); const string IndicatorName = "{6B5B57F6-A9FC-48AB-BA4C-9AB5A16F3745}"; DataResults endItem = new DataResults(); List<DataResults> ListOfResults = new List<DataResults>(); var allData = (from vi in context.vDimIncidents where vi.IncidentDate >= startDate.AddYears(-3) && vi.IncidentDate <= endDate select new { vi.IncidentDate, LocationName = vi.LocationCode, GroupingName = vi.Location, vi.ThisIncidentIs, vi.Location }); var finalResults = (from a in allData group a by new { a.IncidentDate.Year, a.IncidentDate.Month, a.LocationName, a.GroupingName, a.ThisIncidentIs, a.Location } into groupItem select new { Year = String.Format("{0}", groupItem.Key.Year), Month = String.Format("{0:00}", groupItem.Key.Month), groupItem.Key.LocationName, GroupingName = groupItem.Key.GroupingName, Numerator = groupItem.Count(), Denominator = mergedDict[Tuple.Create(groupItem.Key.Year, groupItem.Key.Month, groupItem.Key.LocationName)].TotalHours, IndicatorName = IndicatorName, }).ToList(); for (int counter = 0; counter < finalResults.Count; counter++) { var item = finalResults[counter]; endItem = new DataResults(); ListOfResults.Add(endItem); endItem.IndicatorName = item.IndicatorName; endItem.LocationName = item.LocationName; endItem.Year = item.Year; endItem.Month = item.Month; endItem.GroupingName = item.GroupingName; endItem.Numerator = item.Numerator; endItem.Denominator = item.Denominator; } foreach(var item in mergedDict) { if(!ListOfResults.Exists(l=> l.Year == item.Key.Item1.ToString() && l.Month == item.Key.Item2.ToString() && l.LocationName == item.Key.Item3)) { for (int counter = 0; counter < finalResults.Count; counter++) { var data = finalResults[counter]; endItem = new DataResults(); ListOfResults.Add(endItem); endItem.IndicatorName = data.IndicatorName; endItem.LocationName = item.Key.Item3; endItem.Year = item.Key.Item1.ToString(); endItem.Month = item.Key.Item2.ToString(); endItem.GroupingName = data.GroupingName; endItem.Numerator = 0; endItem.Denominator = item.Value.TotalHours; } } } return ListOfResults; } ``` The error occurs here: ``` Denominator = mergedDict[Tuple.Create(groupItem.Key.Year, groupItem.Key.Month, groupItem.Key.LocationName)].TotalHours, ``` I do not understand why it is not present in the key. The key consists on an `int, int, string (year, month, location)` and that is what I have assigned it. I've looked at all of the other threads concerning this error message but I didn't see anything that applied to my situation. I was unsure of what tags to put on this but from my understanding the dictionary was created with linq to xml, the query is linq to sql and it's all part of C# so I used all the tags. if this was incorrect then I apologize in advance.