MVC 3: HTML.DisplayFor and the Lambda expression - c#

I have found other topics regarding this, but none of them made me understand what I'm trying to figure out.
I am studying MVC 3 and I have problems wrapping my head around the Lambda expressions that go with #HTML.DisplayFor() in the scaffolding template.
I am working with the MvcMusicStore sample application, the view created by the scaffolding looks like this:
#model IEnumerable<MvcMusicStore.Models.Album>
...
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Genre.Name)
</td>
I understand that DisplayFor is a extension method to HTML and that it takes an expression as a parameter. I researched Lambda expressions and understand them in other contexts such as with enumerable strings. For example:
cities.Where(s => s.StartsWith("L"));
What boggles my mind is, that in the second example, the first part of the expression (s) is actually used in the second part (s.startswith..), so it makes sense and I get what the program is doing with it. But this is not the case in the MVC 3 view. modelItem is not beeing used anywhere. I tried renaming just "modelItem" to whatever like so:
#Html.DisplayFor(iAmWearingShortPantsToday => item.Genre.Name)
This code works just as well, displaying all the items and values correctly. This makes me doubt that in this case "modelItem" is actually used for anything. But then, why is it there?
I have also tried to translate this Lambda expression into a delegate (which I believe it is the short form for), like this:
#(Html.DisplayFor(delegate(IEnumerable<MvcMusicStore.Models.Album> modelItem) { return item.Genre.Name; }))
This however, does not work. The resulting error is:
CS1946: An anonymous method expression cannot be converted to an expression tree
Hence my question:
What is "modelItem" good for? What does HTML.DisplayFor() do with modelItem? Are there other cases maybe, where this first part of the expression becomes significant?
If it's possible to translate this expression into a proper delegate, that might also help me to understand what's exactly going on.
Thanks very much

I understand your confusion. Actually modelItem variable is never used in the lambda expression. What is used is the item variable which is captured in a closure from the outer context. The item variable is simply the local variable defined in the foreach loop.
By the way when you see an expression like modelItem => item.Genre.Name in ASP.NET MVC view, that's usually a sign for a bad coding practice. Avoid it. The internet is swarming with such bad examples. Really. I am sick of seeing this. Please help me eradicate this practice.
Here's an alternative method in which the variable is actually used:
#model IList<MvcMusicStore.Models.Album>
...
#for (var i = 0; i < Model.Count; i++) {
<tr>
<td>
#Html.DisplayFor(x => x[i].Genre.Name)
</td>
...
</tr>
}
And even better. Why write loops at all? When we can directly use editor/display templates:
#model IEnumerable<MvcMusicStore.Models.Album>
...
#Html.DisplayForModel()
and then define a display template that will automatically be rendered by ASP.NET MVC for each element of the model collection (~/Views/Shared/DisplayTemplates/Album.cshtml)
#model Album
<tr>
<td>
#Html.DisplayFor(x => x.Genre.Name)
</td>
...
</tr>

Not sure I can answer your question, but the right part is used to tell DisplayFor which property is will create a display for. It is used to determine the type of the property (used to select how to display the value) and the value.
If used in a TextBoxFor, it is also used to retrieve the name of the property, so the textbox can get the proper name.
I think that the expression in a lot of the html-helper extension methods is actually a bit odd, but it's the easy way of letting the code get all these information, without you having to write it down as string parameters (that would fail if you changed anything, without giving a compile-time error).

Related

What does DisplayNameFor do?

I'm learning .NET Core with Razor pages, using one of the official tutorials here, and I'm having trouble with this code:
#Html.DisplayNameFor(model => model.Movie[0].Title)
The tutorial says:
The DisplayNameExtensions.DisplayNameFor HTML Helper inspects the Title property referenced in the lambda expression to determine the display name. The lambda expression is inspected rather than evaluated. That means there is no access violation when model, model.Movie, or model.Movie[0] is null or empty. When the lambda expression is evaluated, for example, with #Html.DisplayFor(modelItem => item.Title), the model's property values are evaluated.
Which I can't make heads or tails of. What does inspect mean here? Does it mean the lambda function runs in an try/catch, to prevent the access violation errors that the docs speak of? What does it mean exactly?
And in what important way is the second example (with DisplayFor) different? It uses DisplayFor instead of DisplayName, and another change is that it uses ModelItem instead of model. I don't know where it would get ModelItem from, model is made available by #model (...) at the op of the razor page but how ModelItem gets here is not clear to me.
The docs for DisplayNameForare here, but the tutorial links to the non-core docs here, both of which are too terse for me to make much sense of.
DisplayNameFor will look for the Name property of the Display property attribute and print it to your razor page (or the property name itself, if it can't find it). So, if your model is
class Foo
{
[Display(Name="My name")]
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
It will display My name for Prop1 and Prop2 for Prop2.
The part about it being inspected rather than evaluated, means that it will look at your model definition, it will not try to get a value, so if the model value is null, it will not throw.
On the other hand, DisplayFor will print the value of the selected property, applying any format described by DisplayFormatAttribute, so the model is evaluated and cannot be null.
Typically you will use DisplayNameFor to build the table headers and DisplayFor to build the table data.
What does inspect mean here?
DisplayNameFor is used to get the name of the property and not the value of it, so you will get Title and not Pets 2. In the second case the lambda expression would be evaluated - the first element of the movie list would be retrieved and then its title, that is not happening here.
The difference between evaluating and inspecting is similar to that between reading a book vs turning it around to find out who published it.
2)
And in what important way is the second example (with DisplayFor) different?
DisplayFor is used to format the data itself (Pets 2 and not the Title). In this case there is not much formatting involved because it is a string, but if it was a number for example you could specify how many digits you want to see ect.
3)
I don't know where it would get ModelItem from
ModelItem it is just the name of the variable to pass into the function. modelItem => item.Title is an equivalent of MyFunction(Movie modelItem){return item.Title;} modelItem can be called anything else, particularly as they don't even use it in the function itself and use item directly.

Why can I use any letter before => in lambda expression, how it would translate to standard syntax [duplicate]

This question already has answers here:
I want to understand the lambda expression in #Html.DisplayFor(modelItem => item.FirstName)
(4 answers)
Closed 7 years ago.
I have tried tp understand lambda expressions for a long time(many attempts).
Inside Razor view I have a foreach loop over the ICollection<Comment> Comments where Model is of class Product.
#foreach (var comment in Model.Comments) {
<p> #Html.DisplayFor(m => comment.TimeStamp)</p>
}
I don't understand why I can use any letter before => inside that expression and it still works. If that does not matter why do I have to write anything before =>?
How it would translate(if it was possible) to the standard method syntax?
In your example, m is the parameter to your function, which is used to pass the value of comment in to your function. However, with the lambda, you still have access to comment, which you are using directly. Just because you do not use the parameter doesn't mean you can do away with it though.
As a result, you could rewrite your code snippet as:
#foreach (var comment in Model.Comments) {
<p> #Html.DisplayFor(m => m.TimeStamp)</p>
}
It important to understand that lambda is in its essence anonymous function declaration.
1) I don't understand why I can use any letter before => inside that expression and it still works
m is just parameter name. You can write #Html.DisplayFor(m => m.TimeStamp) or #Html.DisplayFor(i => i.TimeStamp) and it would be (in this case! - see this) functionally equal to your code (but more efficient)
Reason why your code works is because m parameter is not used in any way and code inside lambda (which is effectively function body) is using variable declared outside the function (code after =>). This is called closure\captured variable - it's less efficient, have many pitfalls but can be extremely useful...
2) If that does not matter why do I have to write anything before =>?
Because #Html.DisplayFor method is declared to take Func<TModel, TValue> parameter which you can read as 'function which takes one argument (model) and returns single value'
3) Using normal (anonymous) method syntax:
#foreach (var comment in Model.Comments) {
<p> #Html.DisplayFor(delegate(CommentsType c) { return c.TimeStamp; })</p>
}
....where CommentsType is type of Model.Comments property

Using model objects in cshtml file

I am a newbie to MVC 4, (after 10 yrs of webforms) and have a question that I have not been able to figure out.
When writing code in the cshtml file, I am walking through a tutorial that has the following line:
#Html.DisplayNameFor(model => model.City)
What does the model => model.City imply? Why can't I use #Html.DisplayNameFor(model.City) ? I understand this is Linq query, but I would like to understand why would I need the model goes to model.city ?
Generally, that is called a lambda expression.In your scenario, you are telling the DisplayNameFor method that "take my model, and create a display element for this property.".You can't use model.City, because it just returns the value of the property.The method needs more than that in order to create a display element for your property.For example, it needs to know it's type and also it's attributes (like DisplayName attribute) and then it creates a display element for your element(it should be label I guess) .
DisplayName method is doing that using Expression Trees.The method takes an Expression<Func<TModel, TValue>> and uses it to get the name, value and the metadata information (attributes) about your property.
If you want to use model.City you can still use it, but then you won't need the functionality that DisplayNameFor provides.If you just need to display value of the property you can always do it like this:
<label> #model.City </label>
I understand this is Linq query,
Btw, this is incorrect, that is not a LINQ query.That is just an extension method.

Why does my MVC razor view does not want to instantiate a ResourceManager

I'm writing this code on an MVC 3 view:
<table>
<tr>
<th>#SecondIndexStrings.Activity_ActivityName</th>
<th>#SecondIndexStrings.Activity_DateStarted</th>
<th>#SecondIndexStrings.Activity_ProficiencyLevel</th>
</tr>
#foreach (var activity in Model.Activities)
{
<tr>
<td>#activity.ActivityName</td>
<td>#activity.DateStarted.ToShortDateString()</td>
<td>#new ResourceManager(typeof(IndexStrings)).GetString(activity.ProficiencyLevel.ToString())</td>
</tr>
}
</table>
For some reason the line with "#new ResourceManager..." is not recognized as valid and it gives me a Compilation Error whenever I run it.
If I only write #ResourceManager the text turns blue indicating that is recognized as a valid class, the same for the IndexStrings resource but, with the whole thing it seems that it doesn't know that those are supposed to be classes.
What am I doing wrong?
Thank you.
Because of the white-space, you must use parenthesis to help it see where your expression starts/ends:
#(new ResourceManager(typeof(IndexStrings)).GetString(activity.ProficiencyLevel.ToString()))
You might also want to consider adding a helper method, perhaps as an extension-method on HtmlHelper, so you can use something like:
#Html.Resource<IndexStrings>(activity.ProficiencyLevel)
or similar, which might look something like:
public static string Resource<T>(this HtmlHelper html, object key) {
return new ResourceManager(typeof(T)).GetString(key.ToString());
}

ASP.NET MVC strongly typed view convert from C# to VB.NET

I'm starting to learn ASP.NET MVC and since I work in a VB.NET shop I'm converting an example from C#. I'm trying to implement a strongly typed view and the example I'm looking at shows the following:
<tr>
<td>Name:</td>
<td><%=Html.TextBox(x => x.Name)%></td>
</tr>
I've come up with the following in VB.NET:
<tr>
<td>Name:</td>
<td><%=Html.TextBox((Function(x As Contact) x.Name).ToString)%></td>
</tr>
Is this conversion correct? This seems really cumbersome (I know, I know, VB.NET is more cumbersome than C#, but I have no choice in the matter). If it is correct, is it the best way?
Why the call to ToString ? The exact conversion is this one :
<tr>
<td>Name:</td>
<td><%=Html.TextBox(Function(x) x.Name)%></td>
</tr>
You probably have an extension method for HtmlHelper somwhere else, since there is no built-in overload for TextBox that takes a Func<Contact, string> as a parameter... So you need to convert that method as well
I'd think (x As Contact).Name would be sufficient, although it has been a while since I tried this with VB.NET...

Categories

Resources