ASP.NET MVC 2 DisplayFor() - c#

I'm looking at the new version of ASP.NET MVC (see here for more details if you haven't seen it already) and I'm having some pretty basic trouble displaying the content of an object.
In my control I have an object of type Person, which I am passing to the view in ViewData.Model. All is well so far and I can extract the object in the view ready for display. What I don't get though, is how I need to call the Html.DisplayFor() method in order to get the data to screen. I've tried the following...
<%
MVC2test.Models.Person p = ViewData.Model as MVC2test.Models.Person;
%>
// snip
<%= Html.DisplayFor(p => p) %>
but I get the following message:
CS0136: A local variable named 'p' cannot be declared in this scope because it would give a different meaning to 'p', which is already used in a 'parent or current' scope to denote something else
I know this is not what I should be doing - I know that redefining a variable will produce this error, but I don't know how to access the object from the controller. So my question is, how do I pass the object to the view in order to display its properties?
N.B. I should add that I am reading up on this in my limited spare time, so it is entirely possible I have missed something fundamental.
TIA

Html.DisplayFor can be used only when your View is strongly-typed, and it works on the object that was passed to the View.
So, for your case, you must declare your View with the type Person as its Model type, (e.g. something.something.View<Person>) (sorry, I don't remember the exact names, but this should make sense), and then when calling Html.DisplayFor(p => p), p would take the value of the passed model (Person) value into the view.
Hope that made sense.

p is already a variable name; and variable names have to be unique throughout the current scope. Therefore displayFor(p=>p) is not valid, as you're declaring a new variable 'p' there. That way the compiler doesn't know wether to use your Person p, or the (p =>) variable.
So just rename it to
<%= Html.DisplayFor(person => person) %>

Related

How to access properties in the model passed to the view?

I'm sending the following model to my view.
return View(new { url = returnUrl });
In the view, I'm don't want to specify any particular class for my object (since I wish to keep it flexible and elastic for now). So, the #Model is the apparently an object and as such, it's got no url property. Hence the error.
Additional information: 'object' does not contain a definition for 'url'
Now, I do know that the thing inside the object has url property. I have assigned it to it and I also see it when watching the variable as the exception's been thrown.
My question is how to access the field. Is my only option declaring a class and type the model using #model Something? I can't use as keyword to type it to var...
In "plain" C# we can do something like this.
var some = new {thing = "poof"};
string output = some.thing;
How do I do the equivalent of it in CSHTML file under Razor?
Strongly-typed view models are the way to go. Create a type that suits the needs of the view and treat reusability/duplication as a secondary concern.
However, let me explain why your attempt did not work.
It is legal to pass an anonymous type--even between assemblies[1]--as long as it is cast to object. In fact, the MVC framework assemblies consume anonymous types in many helper methods. Those anonymous types are then evaluated using reflection (optimized by caching).
1: I believe there are some caveats to this, and it certainly isn't good practice in most cases.
A view is compiled into a class so that it can be executed. Part of the class's contract is the type of model it expects, as indicated by #model in your view markup.
This presents a problem with anonymous types, as you cannot indicate their type in your view and type object doesn't expose the properties you set when declaring the type. Thus, you end up with a view that only knows that its model is an object.
Again, use strongly-typed models, or the ViewBag if you truly only need one or two values.
However, to prove that the anonymous type can be passed to the view, look at this (horrible) example:
Controller
return View( new { property1 = "hello world"} );
View
#model object
#{
var rvd = new RouteValueDictionary( Model );
}
#rvd["property1"]
We passed an anonymous type to the view as an object, and then read the object's properties using RouteValueDictionary.
You can use ViewData and ViewBag to send objects to the view page, in your case you can write in the controller something like this:
ViewData["url"] = url ; //Or whatever
return View();
Now in the view you can simply use your object example:<div>#ViewData["url"]</div>
But mainly in MVC it is more recommended to use strongly typed View Models
You may want to look into using the dynamic type in C#. See https://msdn.microsoft.com/en-us/library/dd264736.aspx for details.
While the standard would be to use a strongly-typed view model, there are some scenarios where you might want to use dynamic as your model type (or as a property of your strongly-typed view model), such as in a CMS where the properties are built dynamically by the CMS Provider.
Example view:
#model dynamic
<p>
Url: #Model.url
</p>

MVC Razor view nested foreach's model

Imagine a common scenario, this is a simpler version of what I'm coming across. I actually have a couple of layers of further nesting on mine....
But this is the scenario
Theme contains List
Category contains List
Product contains List
My Controller provides a fully populated Theme, with all the Categories for that theme, the Products within this categories and the their orders.
The orders collection has a property called Quantity (amongst many others) that needs to be editable.
#model ViewModels.MyViewModels.Theme
#Html.LabelFor(Model.Theme.name)
#foreach (var category in Model.Theme)
{
#Html.LabelFor(category.name)
#foreach(var product in theme.Products)
{
#Html.LabelFor(product.name)
#foreach(var order in product.Orders)
{
#Html.TextBoxFor(order.Quantity)
#Html.TextAreaFor(order.Note)
#Html.EditorFor(order.DateRequestedDeliveryFor)
}
}
}
If I use lambda instead then then I only seem to get a reference to the top Model object, "Theme" not those within the foreach loop.
Is what I'm trying to do there even possible or have I overestimated or misunderstood what is possible?
With the above I get an error on the TextboxFor, EditorFor, etc
CS0411: The type arguments for method
'System.Web.Mvc.Html.InputExtensions.TextBoxFor(System.Web.Mvc.HtmlHelper,
System.Linq.Expressions.Expression>)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
Thanks.
The quick answer is to use a for() loop in place of your foreach() loops. Something like:
#for(var themeIndex = 0; themeIndex < Model.Theme.Count(); themeIndex++)
{
#Html.LabelFor(model => model.Theme[themeIndex])
#for(var productIndex=0; productIndex < Model.Theme[themeIndex].Products.Count(); productIndex++)
{
#Html.LabelFor(model=>model.Theme[themeIndex].Products[productIndex].name)
#for(var orderIndex=0; orderIndex < Model.Theme[themeIndex].Products[productIndex].Orders; orderIndex++)
{
#Html.TextBoxFor(model => model.Theme[themeIndex].Products[productIndex].Orders[orderIndex].Quantity)
#Html.TextAreaFor(model => model.Theme[themeIndex].Products[productIndex].Orders[orderIndex].Note)
#Html.EditorFor(model => model.Theme[themeIndex].Products[productIndex].Orders[orderIndex].DateRequestedDeliveryFor)
}
}
}
But this glosses over why this fixes the problem.
There are three things that you have at least a cursory understanding before you can resolve this issue. I have
to admit that I cargo-culted this
for a long time when I started working with the framework. And it took me quite a while
to really get what was going on.
Those three things are:
How do the LabelFor and other ...For helpers work in MVC?
What is an Expression Tree?
How does the Model Binder work?
All three of these concepts link together to get an answer.
How do the LabelFor and other ...For helpers work in MVC?
So, you've used the HtmlHelper<T> extensions for LabelFor and TextBoxFor and others, and
you probably noticed that when you invoke them, you pass them a lambda and it magically generates
some html. But how?
So the first thing to notice is the signature for these helpers. Lets look at the simplest overload for
TextBoxFor
public static MvcHtmlString TextBoxFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression
)
First, this is an extension method for a strongly typed HtmlHelper, of type <TModel>. So, to simply
state what happens behind the scenes, when razor renders this view it generates a class.
Inside of this class is an instance of HtmlHelper<TModel> (as the property Html, which is why you can use #Html...),
where TModel is the type defined in your #model statement. So in your case, when you are looking at this view TModel
will always be of the type ViewModels.MyViewModels.Theme.
Now, the next argument is a bit tricky. So lets look at an invocation
#Html.TextBoxFor(model=>model.SomeProperty);
It looks like we have a little lambda, And if one were to guess the signature, one might think that the type for
this argument would simply be a Func<TModel, TProperty>, where TModel is the type of the view model and TProperty
is inferred as the type of the property.
But thats not quite right, if you look at the actual type of the argument its Expression<Func<TModel, TProperty>>.
So when you normally generate a lambda, the compiler takes the lambda and compiles it down into MSIL, just like any other
function (which is why you can use delegates, method groups, and lambdas more or less interchangeably, because they are just
code references.)
However, when the compiler sees that the type is an Expression<>, it doesn't immediately compile the lambda down to MSIL, instead it generates an
Expression Tree!
What is an Expression Tree?
So, what the heck is an expression tree. Well, it's not complicated but its not a walk in the park either. To quote ms:
| Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y.
Simply put, an expression tree is a representation of a function as a collection of "actions".
In the case of model=>model.SomeProperty, the expression tree would have a node in it that says: "Get 'Some Property' from a 'model'"
This expression tree can be compiled into a function that can be invoked, but as long as it's an expression tree, it's just a collection of nodes.
So what is that good for?
So Func<> or Action<>, once you have them, they are pretty much atomic. All you can really do is Invoke() them, aka tell them to
do the work they are supposed to do.
Expression<Func<>> on the other hand, represents a collection of actions, which can be appended, manipulated, visited, or compiled and invoked.
So why are you telling me all this?
So with that understanding of what an Expression<> is, we can go back to Html.TextBoxFor. When it renders a textbox, it needs
to generate a few things about the property that you are giving it. Things like attributes on the property for validation, and specifically
in this case it needs to figure out what to name the <input> tag.
It does this by "walking" the expression tree and building a name. So for an expression like model=>model.SomeProperty, it walks the expression
gathering the properties that you are asking for and builds <input name='SomeProperty'>.
For a more complicated example, like model=>model.Foo.Bar.Baz.FooBar, it might generate <input name="Foo.Bar.Baz.FooBar" value="[whatever FooBar is]" />
Make sense? It is not just the work that the Func<> does, but how it does its work is important here.
(Note other frameworks like LINQ to SQL do similar things by walking an expression tree and building a different grammar, that this case a SQL query)
How does the Model Binder work?
So once you get that, we have to briefly talk about the model binder. When the form gets posted, it's simply like a flat
Dictionary<string, string>, we have lost the hierarchical structure our nested view model may have had. It's the
model binder's job to take this key-value pair combo and attempt to rehydrate an object with some properties. How does it do
this? You guessed it, by using the "key" or name of the input that got posted.
So if the form post looks like
Foo.Bar.Baz.FooBar = Hello
And you are posting to a model called SomeViewModel, then it does the reverse of what the helper did in the first place. It looks for
a property called "Foo". Then it looks for a property called "Bar" off of "Foo", then it looks for "Baz"... and so on...
Finally it tries to parse the value into the type of "FooBar" and assign it to "FooBar".
PHEW!!!
And voila, you have your model. The instance the Model Binder just constructed gets handed into requested Action.
So your solution doesn't work because the Html.[Type]For() helpers need an expression. And you are just giving them a value. It has no idea
what the context is for that value, and it doesn't know what to do with it.
Now some people suggested using partials to render. Now this in theory will work, but probably not the way that you expect. When you render a partial, you are changing the type of TModel, because you are in a different view context. This means that you can describe
your property with a shorter expression. It also means when the helper generates the name for your expression, it will be shallow. It
will only generate based on the expression it's given (not the entire context).
So lets say you had a partial that just rendered "Baz" (from our example before). Inside that partial you could just say:
#Html.TextBoxFor(model=>model.FooBar)
Rather than
#Html.TextBoxFor(model=>model.Foo.Bar.Baz.FooBar)
That means that it will generate an input tag like this:
<input name="FooBar" />
Which, if you are posting this form to an action that is expecting a large deeply nested ViewModel, then it will try to hydrate a property
called FooBar off of TModel. Which at best isn't there, and at worst is something else entirely. If you were posting to a specific action that was accepting a Baz, rather than the root model, then this would work great! In fact, partials are a good way to change your view context, for example if you had a page with multiple forms that all post to different actions, then rendering a partial for each one would be a great idea.
Now once you get all of this, you can start to do really interesting things with Expression<>, by programatically extending them and doing
other neat things with them. I won't get into any of that. But, hopefully, this will
give you a better understanding of what is going on behind the scenes and why things are acting the way that they are.
You can simply use EditorTemplates to do that, you need to create a directory named "EditorTemplates" in your controller's view folder and place a seperate view for each of your nested entities (named as entity class name)
Main view :
#model ViewModels.MyViewModels.Theme
#Html.LabelFor(Model.Theme.name)
#Html.EditorFor(Model.Theme.Categories)
Category view (/MyController/EditorTemplates/Category.cshtml) :
#model ViewModels.MyViewModels.Category
#Html.LabelFor(Model.Name)
#Html.EditorFor(Model.Products)
Product view (/MyController/EditorTemplates/Product.cshtml) :
#model ViewModels.MyViewModels.Product
#Html.LabelFor(Model.Name)
#Html.EditorFor(Model.Orders)
and so on
this way Html.EditorFor helper will generate element's names in an ordered manner and therefore you won't have any further problem for retrieving the posted Theme entity as a whole
You could add a Category partial and a Product partial, each would take a smaller part of the main model as it's own model, i.e. Category's model type might be an IEnumerable, you would pass in Model.Theme to it. The Product's partial might be an IEnumerable that you pass Model.Products into (from within the Category partial).
I'm not sure if that would be the right way forward, but would be interested in knowing.
EDIT
Since posting this answer, I've used EditorTemplates and find this the easiest way to handle repeating input groups or items. It handles all your validation message problems and form submission/model binding woes automatically.
When you are using foreach loop within view for binded model ...
Your model is supposed to be in listed format.
i.e
#model IEnumerable<ViewModels.MyViewModels>
#{
if (Model.Count() > 0)
{
#Html.DisplayFor(modelItem => Model.Theme.FirstOrDefault().name)
#foreach (var theme in Model.Theme)
{
#Html.DisplayFor(modelItem => theme.name)
#foreach(var product in theme.Products)
{
#Html.DisplayFor(modelItem => product.name)
#foreach(var order in product.Orders)
{
#Html.TextBoxFor(modelItem => order.Quantity)
#Html.TextAreaFor(modelItem => order.Note)
#Html.EditorFor(modelItem => order.DateRequestedDeliveryFor)
}
}
}
}else{
<span>No Theam avaiable</span>
}
}
It is clear from the error.
The HtmlHelpers appended with "For" expects lambda expression as a parameter.
If you are passing the value directly, better use Normal one.
e.g.
Instead of TextboxFor(....) use Textbox()
syntax for TextboxFor will be like Html.TextBoxFor(m=>m.Property)
In your scenario you can use basic for loop, as it will give you index to use.
#for(int i=0;i<Model.Theme.Count;i++)
{
#Html.LabelFor(m=>m.Theme[i].name)
#for(int j=0;j<Model.Theme[i].Products.Count;j++) )
{
#Html.LabelFor(m=>m.Theme[i].Products[j].name)
#for(int k=0;k<Model.Theme[i].Products[j].Orders.Count;k++)
{
#Html.TextBoxFor(m=>Model.Theme[i].Products[j].Orders[k].Quantity)
#Html.TextAreaFor(m=>Model.Theme[i].Products[j].Orders[k].Note)
#Html.EditorFor(m=>Model.Theme[i].Products[j].Orders[k].DateRequestedDeliveryFor)
}
}
}
Another much simpler possibility is that one of your property names is wrong (probably one you just changed in the class). This is what it was for me in RazorPages .NET Core 3.

C# - Get property in member class using Reflection

SHORT VERSION
What's the best way to use reflection to turn something like string prop = "part1.first_name"; into a System.Reflection.PropertyInfo, so that I can use the GetValue and SetValue functions?
LONG VERSION
I'm using ASP .NET MVC to build a questionnaire for my organization. It's very long, so it's divided into several different pages. Since it's not uncommon for us to get requests like, "Can you move this question to that page, and this other question to another page," I need to build this to be pretty flexible for a junior programmer to change.
My model is a complex class (it's got five member classes that have mostly primitive-typed properties on them).
So, I access it by doing things like Model.part1.first_name or Model.part2.birth_date.
Since the same model is used on all of the pages, but not all of the questions are on every page, I have ActionAttributes that essentially clear out all of the properties that were submitted on the form except for the ones that were displayed on that page (so someone can't inject a hidden field into the form and have the value persist to the database).
I want to make sure that I only save valid field values and don't let the user proceed to the next page until the current one is entirely OK, but I also want to save the values that are valid, even if the user isn't allowed to proceed.
To do this, I have a function that takes two instances of my model class, a reference to the ModelStateDictionary, and a string[] of field names like "part1.first_name" and "part2.birth_date". That function needs to copy all of the values listed in the string array that do not have validation errors from the first (ie, form-submitted) object into the second (ie, loaded from the db) object.
As stated above, what's the best way to use reflection to turn something like "part1.first_name" into a System.Reflection.PropertyInfo, OR, is there a better way to accomplish this?
var infoParts = prop.Split('.');
var myType = Type.GetType(infoParts[0]);
var myPropertyInfo = myType.GetProperty(infoParts[1]);
Assuming "part1" is your type. Although this is very limited and very dependent on the string being in the correct format and the type being in the current scope.
I would probably handle this differently, using data. I would keep, in the database, which step each question belongs to. To render that step, I would select the questions that match that step and have a model that contains a list of question id/question pairs. Each input would be identified by the question id when posted back. To validate, simply compare the set of question ids with the expected ids for that step. This way, to change which question goes in which step is to only change the data in the database.
If you do end up going down that road, you'll need to split the string into parts and recursively or iteratively find the property on the object at each step.
PropertyInfo property = null;
Type type = questionModel.GetType();
object value = questionModel;
object previousObj = null;
foreach (var part in questionId.Split('.'))
{
property = type.GetProperty(part);
previousObj = value;
value = property.GetValue(value,null);
type = value.GetType();
}
// here, if all goes well, property should contain the correct PropertyInfo and
// value should contain that property's value...and previousObj should contain
// the object that the property references, without which it won't do you much good.

How do I reference members of a single object passed to the View?

I'm new to MVC2 in ASP.NET/C#, so please forgive me if I misunderstand something. I have code similar to this in my Controller:
var singleInstance = new Person("John");
ViewData["myInstance"] = singleInstance;
return View();
So in my view, Index.aspx, I want to be able to reference members in that object. For example, Person has a member called Name, which is set in the constructor. In the view I want to get Person.Name from what is stored in the ViewData object. Ex.:
<%= ViewData["myInstance"].name %>
That doesn't work. The only real workaround I've found is to do something like this:
<% var thePerson = ViewData["myInstance"];
print (or whatever the method is) thePerson.Name;
%>
Any help would be much appreciated... This was so much easier in PHP/Zend Framework... sigh
Have a look at this tutorial.
Really you should (as Tommy mentions) pass the model objects through to the View using the return View(foo). But if you must use ViewData (sometimes you need to), simply cast your object back to its original type in your View.
i.e.
<%= (ViewData["myInstance"] as Person).name; %>
You need to cast because ViewData is just a dictionary with objects. Its not strongly-typed, which is why passing straight through to the View using the ActionResult is preferred in most cases.
This should work, but what would be easier in this instance is to make use of the strongly typed views (good benefits in security and a little easier to modify the view with the model data). Then you can do this in your controller:
var singleInstance = new Person("John");
return View(singleInstance);
and this in your view:
<%= html.encode(model.name) %>
In your case, I do use the ViewData object in some instances, and what you have should work. Now, the IntelliSense in Visual Studio will not automatically bring up the properties of a specific ViewData object because you can pass 'anything' in the ViewData. Unless in C#, you have to have this.
<%= ViewData["myInstance"].name; %>
Maybe a C# ASP.MVC'er can weight in on this one
'anything'- just in case there is some object that you can't pass in here
EDIT: To comment on the print or whatever line
You don't have to call any methods to print anything out, you just wrap the variable/object in the <%= %> tags. The html.Encode() method helps to prevent XSS attacks and other nasties in the wild.
Why don't you create a strongly typed view that accepts a "Person"?
In your Controller
var singleInstance = new Person("John");
ViewData.Model = singleInstance;
return View();
In your (strongly typed) View
<%= Html.Encode (Model.name) %>

MVC: model of type Nullable<T>

I have a partial view that inherits from ViewUserControl<Guid?> - i.e. it's model is of type Nullable<Guid>. Very simple view, nothing special, but that's not the point.
Somewhere else, I do Html.RenderPartial( "MyView", someGuid ), where someGuid is of type Nullable<Guid>. Everything's perfectly legal, should work OK, right?
But here's the gotcha: the second argument of Html.RenderPartial is of type object, and therefore, Nullable<Guid> being a value type, it must be boxed. But nullable types are somehow special in the CLR, so that when you box one of those, you actually get either a boxed value of type T (Nullable's argument), or a null if the nullable didn't have a value to begin with. And that last case is actually interesting.
Turns out, sometimes, I do have a situation when someGuid.HasValue == false. And in those cases, I effectively get a call Html.RenderPartial( "MyView", null ). And what does the HtmlHelper do when the model is null? Believe it or not, it just goes ahead and takes the parent view's model. Regardless of it's type.
So, naturally, in those cases, I get an exception saying: "The model item passed into the dictionary is of type 'Parent.View.Model.Type', but this dictionary requires a model item of type 'System.Guid?'"
So the question is: how do I make MVC correctly pass new Nullable<Guid> { HasValue = false } instead of trying to grab the parent's model?
Note: I did consider wrapping my Guid? in an object of another type, specifically created for this occasion, but this seems completely ridiculous. Don't want to do that as long as there's another way.
Note 2: now that I've wrote all this, I've realized that the question may be reduced to how to pass a null for model without ending up with parent's model?
<% Html.RenderPartial("MyView", someGuid ?? new Guid()); %>
UPDATE:
Using editor and/or display templates in ASP.NET MVC 2.0 you can achieve the desired result. Place a Guid.ascx file in the Shared/EditorTemplates folder and include it like this:
<%= Html.EditorFor(x => someGuid) %>
or if the guid is a property of the main model:
<%= Html.EditorFor(x => x.SomeGuid) %>
Now if you put a <%= Model.HasValue %> inside the partial you can get false but not with RenderPartial.

Categories