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?
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))?
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.
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 ?
While your code always runs within the limits of certain scope (class, method, private, etc), the debugger has no such limits
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
Related
I'm not sure what I'm doing wrong here. The default display template for a model I'm using is not being used.
This code is in my main action view:
#if (Model.EmbeddedMediaModels != null)
{
foreach (var mediaItem in Model.EmbeddedMediaModels)
{
BitmapFigureModel bitmap = mediaItem as BitmapFigureModel;
if (bitmap != null)
{
var mm = ModelMetadata.FromLambdaExpression(p => bitmap, this.ViewData);
var modelTypeName = mm.ModelType.Name; // = "BitmapFigureModel"
// Neither resolve the template.
// Html.DisplayFor(m => bitmap);
Html.DisplayFor(m => bitmap, modelTypeName);
}
}
}
The Model.EmbeddedMediaModels property is a collection of EmbeddedMediaModel base types, at present it just contains one object, a BitmapFigureModel which derives from EmbeddedMediaModel.
It's tempting to think that this is confusing matters, but the ModelMetadata instance retrieved is quite able to see the correct BitmapFigureModel model type.
Besides, even if I specify the model type name in the call to DisplayFor it still doesn't work.
And here's proof that a correctly-named display template partial view is in place.
What am I doing wrong?
Contrary to the advice from Brad Wilson (ASP.NET team):
The expression-based versions are primarily used for pulling values
from the model (they are parametrized by the current model, as shown
in the example above). They can also be used for pulling values from
some source other than the model or ViewData (for example, with an
expression like “model => someOtherValue” which ignores the model
entirely). This makes them useful in loops.
http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html
It actually seems that its not possible to "ignore the model entirely". In the comments under my question DaveParsons suggests to experiment by just newing-up a model instance and passing it into DisplayFor, this leads to the error:
Templates can be used only with field access, property access,
single-dimension array index, or single-parameter custom indexer
expressions.
So it appears that I should stop being a smarty-pants and just use Html.Partial as Ehsan Sajjad suggests.
do like this:
#Html.DisplayFor(m => mediaItem.Name)
if you want to load the partial view:
#Html.Partial("~/Views/Shared/DisplayTemplates/BitmapFigureModel.cshtml", mediaItem)
or:
#Html.RenderPartial("~/Views/Shared/DisplayTemplates/BitmapFigureModel.cshtml", mediaItem)
In your BitmapFigureModel.cshtml:
#model BitmapFigureModel
So, vb has the much discussed "on error resume next", right now I really need something like that in a C# razor view.
It probably doesn't exists, buy maybe there is another way to accomplish something similar?
I'm trying to create a simple Razor View that is a read only page just for printing. However this view will have a LOT of fields, several hundred rows like this:
<td class="label">Sales Rep #1:</td>
<td class="val">#Model.salesRep1.name</td>
The problem is that salesRep might be null, and this will throw an error. The amount of code on the page will drastically increase if I have wrap each field in an #{if(Model.salesRep1.firstName != null){}} statement.
It would be super slick to just add on error resume next to the top of the page and not worry about the nulls. Is there any similar solution?
Thanks,
View's should only contain view logic, and even that should only extend to outputting properties from the ViewModel.
The controller is often responsible for building up this viewmodel, and it is at that point that you should default any properties where you want to ensure that they are not null when trying to display them.
So for example, in a controller you might have:
var emptySalesRep = new SalesRep();
var model = new MyModel();
model.salesRep1 = repository.GetSalesRep1() ?? emptySalesRep;
Now, it might be that you have a lot of this, so you might choose to instead leave salesRep1 null, and have another way of dealing with this in the View without resporting to lots of if(Model.salesRep1 != null){ ... }.
An extension method could be used (although I prefer the approach above)
public static string DisplayForWhenNotNull<T>(this HtmlHelper html, Func<T> obj, Func<T,object> prop)
{
var item = obj();
if(item == null)
{
return null;
}
return prop(item);
}
Usage:
<td class="label">Sales Rep #1:</td>
<td class="val">#Html.DisplayForWhenNotNull(() => Model.salesRep1, sr => sr.name)</td>
Instead of checking for nulls in the view, or try to ignore errors, I'd use a viewmodel and make sure that salesRep isn't null in there.
Recently I have faced with a very strange problem. My Action method must return JsonResult,and all is good untill the last break point before return (At this moment I have correct json result).Then in browser console I see error 500 (Internal server error).Nothing exception in debugger.When I start to chek every step in debugger with F10,F11 I have noticed something strange.Unexpected infinitive invokes to my model properties (sometimes to model properties,sometimes infinitive invoking functions and then model proerties).I decide that this infinitive loop provoked error (but I still misunderstanding why I couldn't see it in debugger - perhaps this is aspect of IIS debugging).Code hasnt got weak places (I dont show it because it will take much more than few space).I know that my question is not constructive in stackoverflow terminalogy but I hope that somebody has encountered the same problem.I need only ideas.Thanks.
SOLUTION
As noticed #mreyeros and # LastCoder self referencing can be the reason of such behavior.I have cheked my model in details and found this place:
private IEnumerable<CollegeEstimateModel> _initialModels;
public IEnumerable<CollegeEstimateModel> InitialModels
{
get { return _initialModels = _initialModels ?? CreateInitialModelsList(); }
}
where CollegeEstimateModel contains above properties
I have added [ScriptIgnore] attribute and all become ok.
You should start by checking to see if the model that you are trying to serialize to your JSON result does not contain a property with a self referencing property. For example you have an Order object that contains a collection of details. The detail record has a navigation property back up to the parent order, thus causing a loop during serialization of the order object. This is just a guess of course, but hope that it helps
I have a large WPF application that uses the MVVM design pattern and asynchronous data access methods. It uses the old style asynchronous code with callback handlers and the IAsyncResult interface... here is a typical example:
function.BeginInvoke(callBackMethod, asyncState);
Then , in the view model, I have the following callback handler:
private void GotData(GetDataOperationResult<Genres> result)
{
UiThreadManager.RunOnUiThread((Action)delegate
{
if (result.IsSuccess) DoSomething(result.ReturnValue);
else FeedbackManager.Add(result);
});
}
The RunOnUiThread method is basically the following:
public object RunOnUiThread(Delegate method)
{
return Dispatcher.Invoke(DispatcherPriority.Normal, method);
}
This problem only affects one view model, the one that enables the users to edit the Release objects. On the related view, certain collections that populate ComboBoxes are requested from the database when it is first loaded. Let's simplify this saying that there is just one collection called Genres. After the data arrives in the view model, it is handled like so:
private void GotGenres(GetDataOperationResult<Genres> result)
{
UiThreadManager.RunOnUiThread((Action)delegate
{
if (result.IsSuccess) Genres.AddEmptyItemBefore(result.ReturnValue);
else FeedbackManager.Add(result);
});
}
When the collection is present and a Release object has been selected in the UI, I have the following code selects the current Release.Genre value from the collection:
if (!Release.Genre.IsEmpty && Genres.ContainsItemWithId(Release.Genre.Id))
Release.Genre = Genres.GetItemWithId(Release.Genre);
At this point, I should note that this all works fine and that this is the only line that references the Release.Genre property from the view model.
My particular problem is that sometimes the Release.Genre property is set to null and I can't work out how or from where. >> Edit >> When I put a break point on the property setter, << Edit << the Call Stack provides no real clues as to what is setting the null value, as there is only a [Native to Managed Transition] line. On selecting the Show External Code option from the Call Stack window, I can see basic asynchronous code calls:
Now I can confirm the following facts that I have discovered while attempting to fix this problem:
The one line that references the Release.Genre property is not setting it to null.
The call to Genres.AddEmptyItemBefore(result.ReturnValue) is not setting it to null... this just adds the result collection into the Genres collection after adding an 'empty' Genre.
The Release.Genre property is sometimes set to null in or after the call to Genres.AddEmptyItemBefore(result.ReturnValue), but not because of it... when stepping through it on a few occasions, execution has jumped (in an unrelated manner) to the break point I set on the Release.Genre property setter where the value input parameter is null, but this does not happen each time.
It generally happens when coming from a related view model to the Release view model, but not every time.
The related view model has no references to the Release.Genre property.
To be clear, I am not asking anyone to debug my problem from the sparse information that I have provided. Neither am I asking for advice on making asynchronous data calls. Instead, I am really trying to find out new ways of proceeding that I have not yet thought of. I understand that some code (almost certainly my code) somewhere is setting the property to null... my question is how can I detect where this code is? It does not appear to be in the Release view model. How can I continue to debug this problem with no more clues?
I usually use Flat File, XML or Database logging for debugging purpose. I created those Log classes for logging purpose, so that I can call it from every applications I develop.
For database logging, you can do it as simple as:
void WriteLog(string log){
// Your database insert here
}
Maybe you need datetime and other supporting information, but it's up to the developer. For simple flat file logging is:
void WriteLog(string log){
using(TextWriter tx = new StreamWriter("./Log/" + DateTime.Now.ToString() + ".txt", false)){
tx.WriteLine(log);
}
}
You can use the logging in your application in both ways:
1: Method call
WriteLog((Release.Genre == null).ToString());
if (!Release.Genre.IsEmpty && Genres.ContainsItemWithId(Release.Genre.Id))
Release.Genre = Genres.GetItemWithId(Release.Genre);
2: Add it in your Release.Genre set (or get) property
public class Release{
private Genre _genre=null;
public Genre Genre{
get{
WriteLog((_genre == null).ToString());
return _genre;
}
set{
WriteLog((_genre == null).ToString());
_genre = value;
}
}
}
With this, you can try to get the call sequence, whether the Release.Genre is being set in other places before, during call, etc.
Please note I just giving the general image of building logging. Please expect errors. However, it is developer's responsibility to develop the Logging acitivities to meet requirement.
I'm trying to create a strongly typed model for one of my Views in MVC. The model is the result of a LINQ GroupBy query so it is the type shown below (grouping employees by first letter of surname).
#model IQueryable<IGrouping<string, Employee>>
I'm unsure why but it doesn't let me have a model of this type. The error message I get is:
An opening "<" is missing the corresponding closing ">". Which is incorrect.
I know I can create a view specific model and populate that instead but I'd like to know why this model doesn't seem to work?
By default, a very limited set of namespaces are available for direct use in razor views. Try to expand it to fully qualified names and see if the problem persists:
#model System.Linq.IQueryable<System.Linq.IGrouping<string, Name.Space.Employee>>
I don't know why you'd be getting this error, since you appear to be using correct Razor code. It's possible that there's actually a bug elsewhere in the page that is being made manifest through this incorrect error message.
A workaround, which may help you determine the real source of the bug, would be to create your own strongly-typed model class, which could have this data as its property:
public class EmployeeListViewModel
{
public IQueryable<IGrouping<string, Employee>> EmployeesByCompanyTitle {get;set;}
}
(There are those who would argue that this is a better approach anyway, since you can now add information to your view model more easily.)