7

Just came across an interesting effect while debugging a View. The scenario is easy to reproduce - I have a breakpoint in a View, in the Watch window I add ViewBag.ViewData and the value is null. However, if I just add ViewBag and expand the object, I can see the ViewData and it is not null. I can also successfully expand it and see its properties.

Can anyone explain whether it's a bug or what causes this behavior?

ViewBag.ViewData in Watch window

EDIT

ViewBag.ViewData is actually null. E.g. if I have this code in the View:

if (ViewBag.ViewData == null)
{
    <span>ViewBag.ViewData is null</span>
}

it displays the span. So the weird part is that I can expand it in the watch window and see the properties.

EDIT2

In response to @Darin Dimitrov's answer - I tried to reproduce this behavior with a custom test class and I get a RuntimeBinderException when trying to access the private property: 'SomeClass.SomeProperty' is inaccessible due to its protection level:

public class SomeClass
{
    private string SomeProperty;
}

dynamic dynamicObject = new SomeClass();
if (dynamicObject.SomeProperty == null)
{
    Console.WriteLine("dynamicObject.SomeProperty is null");
}

In this case, shouldn't I be getting the same exception when accessing ViewBag.ViewData in the View (the line with if (ViewBag.ViewData == null))?

Anthony Mastrean
  • 21,850
  • 21
  • 110
  • 188
Yakimych
  • 17,612
  • 7
  • 52
  • 69

4 Answers4

6

What you see in the debugger/watch window is the private ViewData property of the ViewBag. When you do the test in the view you obviously have no access to this private field and you get null because there is no corresponding public property.

Now do the following test in the view:

@if (null == ViewBag
         .GetType()
         .GetProperty("ViewData", BindingFlags.Instance | BindingFlags.NonPublic)
         .GetValue(ViewBag, null)
)
{
    <span>ViewBag.ViewData is null</span>
}

and you won't see the span.

Of course all this is very funny but when it comes to writing real world and properly architected ASP.NET MVC applications both ViewData and ViewBag have no place. Those two are my worst enemies in ASP.NET MVC application development.

Conclusion: always use view models and strongly typed views and have fun.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • thanks for the answer and it seems to be the most plausible explanation so far (apart from a bug in the Watch/Debugger). However, shouldn't I be getting a `RuntimeBinderException` instead of `null` when I try to access `ViewBag.ViewData` (see **EDIT2** to my question)? And btw, I appreciate you reminding about the evilness of ViewBag and ViewData, but that's not necessary - I am a strong proponent of strongly typed viewmodels myself ;) – Yakimych Apr 28 '11 at 22:07
  • @Yakimych, your EDIT2 doesn't correspond to the real situation in ASP.NET MVC. Look at the source code of the framework. You will discover the `System.Web.Mvc.DynamicViewDataDictionary` type deriving from `DynamicObject` and overriding getting and setting properties which is what ViewBag is. In your EDIT2 you are simply setting the dynamic value to `SomeClass`. I would recommend you downloading and exploring the source code of ASP.NET MVC for getting deeper understanding of the inner workings. – Darin Dimitrov Apr 28 '11 at 22:15
  • yes, exactly - I was looking at the source code when you posted this comment. `DynamicViewDataDictionary` inherits `DynamicObject` and overrides the default `dynamic` behaviour by overriding `TryGetMember` and `TrySetMember`. Haven't gone too deep into the details, but this finally makes sense. Thanks for satisfying my curiosity! +1 and accepted answer! – Yakimych Apr 28 '11 at 22:29
0

I don't have a technical explanation, but I believe it's because ViewBag is dynamic. It's like doing a watch on an expression, you don't see the contents of the resultset without running the expression.

I guess the ViewBag is behaving the same way, because you're going directly to a property that hasn't been loaded, and you're doing it through a debugger, it simply decides not to load that property.

I could be totally wrong of course :D

Just for arguments sake, does it do the same in quick-watch or the immediate window ?

Russ Clarke
  • 17,511
  • 4
  • 41
  • 45
  • no, it seems that `ViewBag.ViewData` is actually `null` (see the updated question). The weird part is why I can expand it in the watch window. – Yakimych Apr 28 '11 at 12:44
0

While your code always runs within the limits of certain scope (class, method, private, etc), the debugger has no such limits

BlackTigerX
  • 6,006
  • 7
  • 38
  • 48
0

why are you referencing ViewBag.ViewData? Just reference ViewData directly. This may work because behind the scenes viewbag uses viewdata you may be looking at an internal representation of ViewData, separate than your dynamimc property of "ViewData"

After testing this out, ViewBag shows up as a nonpublic member... which is what I would expect. System.Web.Mvc.DynamicViewDataDictionary has a non public member ViewData

Adam Tuliper
  • 29,982
  • 4
  • 53
  • 71
  • First of all, I am not referencing `ViewBag.ViewData` - as I mentioned in my question, I just came across this weird situation and got interested to find out whether it's a bug or some obscure feature of dynamic objects. Secondly, could you elaborate on this: `This may work because behind the scenes viewbag uses viewdata you may be looking at an internal representation of ViewData, separate than your dynamimc property of "ViewData"`. I am not sure if that's the punctuation, but I have trouble grasping your line of thought on this one ;) – Yakimych Apr 28 '11 at 22:12
  • And finally: `ViewBag shows up as a nonpublic member... which is what I would expect. System.Web.Mvc.DynamicViewDataDictionary has a non public member ViewData` - that's true and somewhat makes sense (also see @Darin Dimitrov's answer), but should I not be getting a `RuntimeBinderException` when trying to access `ViewBag.ViewData` instead of `null` (see my *EDIT2* to the question)? – Yakimych Apr 28 '11 at 22:13
  • I simply meant the same thing Darrin posted after me.. I just didn't say it as well : ) – Adam Tuliper May 04 '11 at 04:09