Linq and DisplayFor in ASP.Net MVC - c#

I've got a model that has an ICollection of telephone numbers. In my view I want to display the value for the record that is marked as Primary.
#Html.DisplayFor(model => model.Telephones.Where(a=>a.Primary==true).Number)
The .Number is not allowed in the Linq syntax. How would one go about making the above line work or rather what is the proper way to display the number?
I probably should do the logic in the controller and put the value in a view model but it seems like this should be doable.

The Where extension method returns an IEnumerable<T>. If you just want the first item which satisfies the condition, use First:
#Html.DisplayFor(model => model.Telephones.First(a => a.Primary).Number)
If you also need to ensure that exactly one item in the set satisfies the condition (and throw an exception otherwise), consider using Single:
#Html.DisplayFor(model => model.Telephones.Single(a => a.Primary).Number)

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.

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.

Generic use of DropDownListFor with reflections

I've always used DropDownListFor like this
Html.DropDownListFor(m => m.PropertyOfTheModel, SelectionList)
this works fine as long as you know the exact name of the property you're trying to build a dropdown list on (in this case: PropertyOfTheModel).
Now I have a different task. My model contains a fixed property, declared as object and called FormModel. Using reflections assume I'd like to build a dropdown for everyone of the properties contained in FormModel. Thanks to the attributes I've managed to solve the SelectionList part, I now have to write the first argument but I have no idea on how to do it.
foreach (var property in Model.FormModel.GetType().GetProperties())
{
#Html.DropDownListFor(m => m.FormModel.GetType().GetProperty(property.Name), SelectList)
}
The code above is not working: how should I write it? I have no experience in writing lambda expressions: is there any alternative to generate a dropdownlist which automagically gets the correct selected attribute just by passing in the property and the selection list? Or do I have to write the expression? Thanks!
this is the error I get:
Templates can be used only with field access, property access,
single-dimension array index, or single-parameter custom indexer
expressions.
I haven't had time to try this out yet, but the stumbling block may be that the code is trying to bind PropertyInfo, not the actual property on the class. I would give this a shot:
foreach (var property in Model.FormModel.GetType().GetProperties())
{
#Html.DropDownListFor(m => property.Name, SelectList)
}
I think it is as simple as this:
#Html.DropDownList(property.Name, SelectList)
Don't use the DropDownListFor helper, but DropDownList instead.

MVC 3: HTML.DisplayFor and the Lambda expression

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).

UpdateModel is not updating "deep" property

I have an ASP.NET MVC application. At a certain point I get a FormCollection in a Controller method that I want to use to update a model. In the collection not all of the values are properties of that model and the property to be updated is an item from a list, and that list is also an item from another list. Something like this (I hope this is clear):
propertyToUpdate --> model.Items[0].Subitems[0].SomePropertyClass.Value;
I tried this in my Controller:
UpdateModel(model);
The problem is that this is not working and I assume it has something to do with the fact that the reflection is not working. I went searching and stumbled upon this article. So I understand that using the prefix-parameter solves the problem. But not in my case, as the properties lie "deeper" in the model as items from a list.
Does anyone know how I can solve this?
Update:
Here's the EditorTemplate for the property:
#model Q95.Domain.Property
<li>
#Html.DisplayFor(p => p.Description) :
#Html.DisplayFor(p => p.Quantity.Value)
#Html.DisplayFor(p => p.Quantity.Unit.Description)
<br />
#Html.TextBoxFor(p => p.Quantity.Value)
</li>
This template is called like this:
<ul>
#Html.EditorFor(model => model.SegmentRequirement.MaterialRequirements[j].Properties)
</ul>
Is this enough code or is there something still missing?
Update2:
Ok, in all the sub-properties I defined parameterless constructors and now I call:
UpdateModel(segmentRequirement, "SegmentRequirement", form.ToValueProvider());
This updates the model, but everything from MaterialRequirements is re-instantiated... :S
UpdateModel works fine on "Deep properties".
The problem is probably the data in the collection you get isn't equal to the properties names.
Check 3 places to see the values you get from the page
The form values.
The route data
The query string
In exact that order.
The keys should match you model properties names.
Update:
How to match the keys to properties names?
The input id will be the key you will get, change the the ids to match your properties names, or even better, use the HtmlTextBoxFor helper: see this article:
Maybe you should create flattened ViewModel and then use that to populate the view, and later synchronize it with the real model.
Can you show us your model and your view, if you are not using htmlhelper, you then have to understand the naming convention very well in order to make the model binding work with your model. so the first thing in first is to show us your model and view.

Categories

Resources