2

This question is basically the same as this one, although the answer to that person's problem turned out to be a simple trailing space.

My issue is that I'm retrieving data from a web API as dictionary and then trying get the values out of it. I'm using TryGetValue because not every item in the dictionary will necessarily contain every key. For some reason, whilst I can get the value of one key with no problems at all when it's present, for another key TryGetValue always evaluates to false and therefore doesn't return the value, even though I can see in debug that the key is present.

So, this block always retrieves the value of the "System.Description" key if it's present:

string descriptionValue = "";
if (workItem.Fields.TryGetValue("System.Description", out descriptionValue))
{
    feature.Description = descriptionValue;
}

However, this almost identical block NEVER retrieves the value of the "CustomScrum.RoadmapGroup" key:

int RoadmapGroupValue = 0;
if (workItem.Fields.TryGetValue("CustomScrum.RoadmapGroup", out RoadmapGroupValue))
{
    feature.RoadmapGroup = RoadmapGroupValue;
}

As you can see in this screenshot, the dictionary DOES contain a key with a name exactly matching my TryGetValue statement: enter image description here

If I put a breakpoint on the code which should be run if the TryGetValue statement evaluates to true (feature.Description = descriptionValue;) it never gets hit.

The feature.RoadmapGroup variable gets set to 0 for every item in the dictionary.

I've been staring at this for the last two hours at least and I can't see what I'm doing wrong.

Community
  • 1
  • 1
Philip Stratford
  • 4,513
  • 4
  • 45
  • 71
  • What if you try using a string instead? string RoadmapGroupValue = "0"; – Shak Ham Sep 22 '17 at 16:51
  • @ShakSmith I tried that, the `TryGetValue` still evaluates to false and never runs the code it should if it finds the key. – Philip Stratford Sep 22 '17 at 16:55
  • How is the dictionary declared? What is the type of it's values? – René Vogt Sep 22 '17 at 16:59
  • Can you try something like `workItem.Fields["CustomScrum.RoadmapGroup"]` and see if you can retrieve your value? – Sach Sep 22 '17 at 17:02
  • @Sach Ok, if I modify my code so the dictionary only includes items where the "CustomScrum.RoadmapGroup" key exists and then replace the `TryGetValue` block with `feature.RoadmapGroup = (int)workItem.Fields["CustomScrum.RoadmapGroup"];`, I get a `SystemInvalidCastException` error. I'm casting to an integer because feature.RoadmapGroup is an integer. – Philip Stratford Sep 22 '17 at 17:13
  • What exception do you get if you simply try doing: `int RoadmapGroupValue = workItem.Fields["CustomScrum.RoadmapGroup"]` – Rufus L Sep 22 '17 at 17:18
  • Without the cast to an integer it won't even build - Intellisense says "Cannot implicitly convert type 'object' to type 'int'." I remember now that I came across this earlier, but when I'm debugging the value of the key clearly isn't an object, it's just an integer, and in any case, the `TryGetValue` evaluates to false as if it can't find the key in the dictionary, rather than throwing an exception because it can't cast an object as an integer for the value it found. – Philip Stratford Sep 22 '17 at 17:24
  • Please show us what `workItem.Fields` dictionary looks like. Also, if using `var value = workItem.Fields["CustomScrum.RoadmapGroup"]` doesn't give you an error then that means the problem is not with the `Key`. So probably your value is not in a correct format. – Sach Sep 22 '17 at 17:27
  • Make sure `CustomScrum.RoadmapGroup` is convertible to type `int`. Otherwise, the rules that apply to `TryCast`, also apply to `TryGetValue`. – GoldBishop Sep 22 '17 at 17:33
  • Yes, forget the int cast and just do `object` or `var` as the type specified for `RoadmapGroupValue`. The problem here is likely that the key name you're specifying is *not* the same as the one in the dictionary. Not sure how, but at least one character in that string likely has a different value. – Rufus L Sep 22 '17 at 17:33
  • @Sach The declaration of the WorkItem type includes: `public IDictionary Fields {get; set;}`. So if the value is an object and not an integer, how do I get the integer OUT of that object in my `TryGetValue`? And why does the block where my `feature.Description` variable is a string work? And why does the `TryGetValue` evaluate to false rather than throwing an exception about invalid type casting? – Philip Stratford Sep 22 '17 at 17:34
  • [`TryGetValue`](https://msdn.microsoft.com/en-us/library/bb347013(v=vs.110).aspx) will only return false if the key is not found. You should focus on the key name, not the value. – Rufus L Sep 22 '17 at 17:35
  • @Rufus @Sach If I just use `var` then sure enough I don't get any type casting error. However, after a while I copied the name of the key out of the Watch window and pasted it back into my code in the `TryGetValue` statement. It's definitely the same! – Philip Stratford Sep 22 '17 at 17:36
  • @Rufus "TryGetValue will only return false if the key is not found. You should focus on the key name, not the value." - That's exactly what I thought, which is why I haven't been focusing on the type of the value being returned. But as I said, I copied and pasted the key name out of the Watch window showing the data in the returned dictionary and it still doesn't work. – Philip Stratford Sep 22 '17 at 17:37

2 Answers2

3

Here's a scenario where your cast goes wrong.

private void foo()
{
    Dictionary<string, object> dict = new Dictionary<string, object>();
    object obj = new object();
    obj = "1";
    dict.Add("CustomScrum.RoadmapGroup", obj);
    object val;
    var result = dict.TryGetValue("CustomScrum.RoadmapGroup", out val);
    int value = (int)val;
}

TryGetValue() returns true, but the last line (the cast), throws System.InvalidCastException: 'Specified cast is not valid.', although if you use a breakpoint to see the dictionary content it looks like you have something that can be converted to an int. See below:

enter image description here

So I believe that when you add the value to the dictionary, you're not really adding an int but something that looks like an int.

EDIT

I just replaced int value = (int)val; with int value = Convert.ToInt32(val); which converts the value just fine. So you might want to try to use that and see if that works as well.

Sach
  • 10,091
  • 8
  • 47
  • 84
  • And [here is an explanation of the differences between the two castings](https://stackoverflow.com/a/1608828/302248). So looks like you're indeed adding a non-numeric variable as the value `1`, and thus your `(int)` cast goes wrong but `Convert.ToInt32()` doesn't. You'd either want to fix how you add the values to the `Dictionary` or better yet use the proper casting function. – Sach Sep 22 '17 at 17:56
  • Yep, you nailed it. I declared my RoadmapGroupValue variable as an Object instead of an Integer, then in the `TryGetValue` block I cast the output value to Int32 as you suggested and it works. I still think I could have saved myself hours if Visual Studio had thrown a type conversion error rather than just seeming to work with the `TryGetValue` statement simply evaluating to FALSE, seems like a small bug to me. Anyway, thanks for your help! – Philip Stratford Sep 22 '17 at 20:03
0

Are you sure that this "CustomScrum.RoadmapGroup" key is a string? If yes, then make sure that it doesn't contain any special unreadable character. You can just copy this value while debugging, put it in Watch window and check length/bytes representation, then do the same for hand-written string with the same content.

Enumix
  • 1
  • The value is not a string, it's an integer. Is that what you mean? The key name is a string, as far as I can tell - can it be anything else? – Philip Stratford Sep 22 '17 at 17:14
  • What I mean, it's that the key for which you trying get value, can contains some non printable characters. – Enumix Sep 22 '17 at 17:23