Debugging a Tricky Razor Exception
Tuesday, June 04, 2019
The code looked fine, and when executed, he was getting an Index was out of range
exception on the @foreach line. Now... the first thing
that may jump out at you as odd is that you shouldn't even get this exception when using foreach .
Nonetheless, we ran through some usual checks, including making sure that Model.ItemList
did in fact
contain some items (it did: 298 items). We then replaced the foreach loop with
a for loop. Same result. We lowered the iteration count of the loop from
Model.ItemList.Count() (298) to 5 and then to 1. Same result.
At this point, I started wondering if somehow the ItemList data was being modified (by
another thread?) while being iterated over, but this wasn't the case from what I could tell. By
now, I was starting to get a little puzzled – then something tipped me off...
When the exception was raised while running in debug mode, even though the debugger stopped
on the line which now said for ( var idx = 0; idx < Model.ItemList.Count(); idx++ ) ,
the value of idx couldn't be inspected. It was that little "hint" that made me think
that perhaps the exception being reported wasn't correct.
I removed all of the code in the view above and below the loop in question, and the exception
went away. Now, we just had to figure out why... Long story short, a good 50 or 60 lines below
the loop, there was a line of code which said something like:
I checked the model, and sure enough, SomeOtherList contained only three elements.
I was pretty sure that this was the root of the exception. But why had Visual Studio reported
it on the loop 50+ lines above this?
My initial thought was that it had to do with the @Model shorthand versus an actual code block.
To test this, I replaced:
...with...
With this change, the exception was raised on the correct line. My coworker was back in
business, and life was good. I figured the whole experience would make a good blog post, but I
wanted to test my theory as to why this happened a bit further, so I threw together
a small test project.
Our ModelOur Controller Our View The Result Just as before, the exception which actually occurred on line #5 was reported on line #3. Line #3, however, obviously has nothing wrong with it; we clearly passed in a list containing three items. To test my theory about the @Model shorthand, I tried the following:
Here, simply changing value to data-value caused the exception to
be raised on the correct line (#5). Apparently, the value attribute has something
to do with the problem. The following you may recall is what I tried earlier, which ended up
getting the exception thrown on the correct line:
Taking the Html.Raw call out of the braces, however, yields a much different result:
You'll notice above that I swapped the order of the input and span elements
from the earlier examples,
and I'm assuming this is why no relevant source lines was reported –
the runtime can traverse up the source but not down looking for the exception.
That said, the effect of removing the span element completely was stranger still, despite
the span being after the input element:
Given that the problematic behavior only seems to occur with the value attribute
and only when using @Model shorthand (no braces), I'm guessing this may be pretty rare to
see in the wild. I didn't attempt to dig any further into the root cause (nor do I plan to), but
I'm guessing it has something to do with ASP MVC's default model binders.
Whatever the cause, it's something to be aware of, or it could have you scratching your head for
a while if you're ever bitten by it...
|
||||