Default constructor parameter values in ASP.Net MVC? - asp.net

I’m using MVC3, with the Razor syntax, and am going over an example in the Apress book “Pro ASP.Net MVC 3 Framework” (which is very good). In it the author has this bit in a _Layout.cshtml file:
<body>
<div id="header">
#{Html.RenderAction("Summary", "Cart");}
<div class="title">SPORTS STORE</div>
</div>
<div id="categories">
#{ Html.RenderAction("Menu", "Nav"); }
</div>
<div id="content">
#RenderBody()
</div>
</body>
My question has to do with the CartController class. I see that it’s passed a Cart object in its constructor, but the code in the _Layout.cshtml snippet above doesn’t appear to pass one in when RenderAction() is called?
CartController snippet:
public ViewResult Summary(Cart cart) {
return View(cart);
}
When I debug that method a Cart object is in fact passed in, but I don’t see where it’s coming from. This sample code uses Ninject as a dependency injection container but I can’t find any evidence in the configuration that it knows anything about the Cart class. Is there a ‘default value’ mechanism in MVC?

Summary is an action method. It is not a constructor. Therefore, DI doesn't have anything to do with this. Instead, what we are seeing is the behavior of the default model binder. That is to say, the model binder will analyze whatever parameters you declare for your action method and will attempt to populate these arguments with data derived from form or query string parameters and route values.
In your particular scenario, since you are not passing in any route values to your Html.Action call, all it's doing is instantiating the Cart class -- if you inspect it you will presumably see that it's empty (no properties have been set).
However, if you were to pass route values into your Action call, it would populate that object. For example, if Cart contained a property called "Size" of type int, and your action call looked like:
#Html.Action("Summary", "Cart", new { Size = 5 })
Your Cart class would now have its Size property set to 5.
(Note also that it's far cleaner to use the Action method and not the RenderAction method in this case. As you've seen, RenderAction assumes a statement-level call, which requires those ugly enclosing curly braces. Action on the other hand is an expression and returns the actual HTML content inline, and thus can be expressed in a more concise fashion.)

Summary is actually an action method, not a constructor. The data can come from a number of different places:
Route parameters
Query String
Form Variables
Etc.

Related

MVC4 - Relationship between a View and a Controller

I'm having difficulty grasping the concept of MVC within .NET. I'm creating a basic Blog application, mainly to gain insight and skills with MVC. I just don't quite get some of it. The part I am currently unclear about is the relationship between a Controller and View. It would clear the matter up if someone would answer me this.
I have a View called TestA which corresponds to my Controller ControllerTestA. Now I've added the following inside the ControllerTestA.
public ActionResult TestA (){ //do something }
Now I need to know if all my postbacks in whatever form from view TestA will have to go through my TestA Controller method. So essentially I could have different postback with different parameters for different reasons. Some in use with one postback and others in use for another. Is that how it is done?
Would love some assistance here.
You are missing a crucial part of the relationship here, which is routing. You are speaking in terms of WebForms using terms like Postback; don't do that because you'll end up confusing yourself.
The best way to think about MVC is in Requests and Responses.
Let's look at how a request (high level) happens in an MVC application.
Request
Request hits the server with a url ex. /hello/world
That url is used to match any entries in your route table
When a match is found, that route defines basic values like what controller and action should be called.
The controller is created, and that action is called with the route values and other request properties (querystring, session, etc...).
Response
We are now in the controller action, run the code you need to fulfill the request
Pass the data to the View
The view is determined by convention and your ViewEngine
The view is then rendered and written to the response.
The request/response is finished.
Note
This whole process is determined by the route, and the relationship between the controller and view are trivial. Where the form is posted to is determined by you in the view by using helper methods that determine what route to hit in the next request/response flow.
Some Helper Methods.
#Url.Action("index", "home");
#Html.ActionLink("index", "home")
#using (Html.BeginForm("create", "home")) { }
To sum it all up, the relationship between the controller action and view is really facilitate by your routes. Once you have a grasp of how to create them, then you will better understand how to manage the interaction of your application. Hope that helps. :)
There is no such thing as "Postback" in MVC. In contrast to WebForms, a view only renders HTML to be sent to the browser. As soon as any type of request is issued by the browser, it goes to the controller, not to the view.
As for the relationships:
If you define a TestAController (note: Not "ControllerTestA"), it serves the "/TestA/*" URL's. If you have a method TestA in there it will serve "/TestA/TestA".
If your method returns View(someModel) it will look for a view named TestA.cshtml/TestA.aspx, named like your method, within a folder Views\TestA (named like your controller, without the "Controller" suffix)
The view will render the HTML based on the someModel passed by the controller.
Within the view you may call other URL's or post data to some. This closes the circle.
As for the parameters or overloads, there are some restrictions:
You can define overloads for GET vs. POST vs. PUT vs. DELETE. You will need to annotate the methods with the according attributes though.
However you cannot define multiple overloads of the same method name for POSTs with different sets of parameters. You will need to make your POST method signature such that parameters can or cannot be sent to the server:
Example:
public ActionResult TestA(
string someOptionalParameter, int? someOtherOptionalParam)
{
if (string.IsNullOrEmpty(someOptionalParameter)) { ... }
if (someOtherOptionalParam == null) { ... }
}
The model-mapper will set your parameters to null if they are not posted to the server.
Like Khalid already mentioned - you should not mix up the concepts of MVC and WebForms. They are very different. MVC has no such thing as a "view state" which could be posted to the server. It has no WebForm-like lifecycle for the ASPX (or CSHTML) pages.
If you have a form in a view, then that form has a url to which it will post to. This URL is in the Html.BeginForm method in your view.
The form will then be posted to the appropriate controller methond in the approoriate controller
So if BeginForm starts like this:
using (Html.BeginForm("AddProduct", "Product"
Then the action method "AddProduct" in the controller Product (ProductController is the class name) will be called.

Spring MVC checkbox tag bound to collection expects object but validation expects object.id

In my Spring MVC project I have an update page for Class1 that must display a list of form:checkbox tags that is bound to a collection of entities on Class1.
Class1.java:
class Class1 {
private Set<Class2> set;
//... other fields
}
In updateclass1.jspx:
<c:forEach items="${allClass2Instances}" var="class2">
<form:checkbox label="${class2.name}" path="set" value="${class2}"/><br/>
</c:forEach>
With the checkbox tag as above, when I display the page, the checkbox is ticked if the Class2 instance is part of the Set on class1, and unticked if it isn't. But when I hit submit, I get the following error:
Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.Set' for property 'set'; nested exception is org.springframework.core.convert.ConversionFailedException: Unable to convert value "Name 1" from type 'java.lang.String' to type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "Name1"
As far as I can tell when the page is populated, the form:checkbox tag needs an instance to set the correct checked/unchecked value, but on submit the JSP is sending an array of class2.toString() values to a converter that expects the IDs. Conversely when I change the tag to the following:
<form:checkbox label="${class2.name}" path="set" value="${class2.id}"/><br/>
The binding works fine, but when I view the update page the checkboxes are not ticked / unticked correctly because the tag does not know that value being passed in is the object id.
How do I make the binding after submit consistent with what the checkbox tag expects?
In case it matters - this is all inside a scaffolding page generated by Roo.
Figured out how to make this work for now. If anyone comes up with a neater solution please add it and I'll mark it correct instead.
The problem above was being caused by needing ${InstanceOfClass2} to evaluate to different things in different places:
At the time of evaluation of the tag, <form:checkbox> needed an expression that evaluated to an actual instance of Class2
After the tag completed, the value attribute of the generated <input type="checkbox"> tag needs to be equal to a numerical ID field of an instance of Class2
The solution was to add a converter to my Class1Controller, eg:
Converter<Class2, String> getClass2Converter() {
return new Converter<Class2, String>() {
public String convert(Class2 instance) {
return "" + instance.getId();
}
};
}
Thus the expression ${InstanceOfClass2} evaluates to a Class2 instance for the checkbox tag, but when it comes to writing the actual HTML is converted to a numerical ID.
This approach is very messy when working with Roo. All of the other scaffolding relating to Class1 then wants to use this same Converter, so I started seeing a whole bunch of IDs everywhere that you would want to see Class2.name or other such fields. I solved this by modifying the Spring Roo <field:display> custom tag - added an attribute fmtCollectionToString that if present forces the tag to evaluate collections by iterating them and calling toString on each element, instead of calling spring:eval on the whole collection, which also seems to end up with the Converter being invoked.
Like I say, neater solutions greatly appreciated! If there's a way of making converters behave differently in different circumstances, for instance - still want to hear it.

Asp.net mvc 2 model binders what's behind scene

Im starting with MVC2 and i have a simple question:
If i have a typed view with a form inside, and this textbox created with lambda expressions:
<%: Html.TextBoxFor(e => e.Name)%>
When i submit this form the default model binder take the request form, takes the model typed to the view, serialize the data posted (as a this model) and pass it to an action of my controller.
To try to explain myself better, lets imagine that i have a url like localhost/edittestmodel/ID/1 and i have in my controller action the following code:
public ActionResult Edit(int id)
{
TestModel testmodel=new TestModel();
testmodel.Name="texttorenderintotextbox";
//whats the class that place the testmodel properties into the view?
return View(testmodel);
}
What's the responsable class for place the Name property of my testmodel object into the textbox
<%: Html.TextBoxFor(e => e.Name)%>
Thanks in advance.
Best Regards.
Jose.
It's the TextBoxFor helper method that's responsible for generating the input field from the lambda expression.
View's don't have anything to do in POST request and model binding
When you have strong type views, model type is barely used to have the simplicity of code intellisense in view's code (so you can use lambda expressions like in your example).
But when you POST data back, nothing gets checked against the view. It gets checked against controller action parameters though. And if there's a parameter with a certain custom model binder type, that particular model binder is used to process incoming data.
But to answer your question: TextBoxFor checks your strong type view's model and generates particular textbox with correct name attribute. So data from it will be posted back under the correct form field name.
To go even deeper. It's the view engine that parses view's ASPX code and runs all server side scripts including Html.TextBoxFor() call.

How to create a simple ASP.NET MVC action method that accepts HTTP-POST data?

i wish to have a simple Action in my controller that accepts a few optional values and some integer values.
this is my route i wish to have:
HTTP.POST
/review/create
and this is the Action method i would like...
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult Create(int userId,
int addressId,
byte baseScore,
byte reviewType,
string subject,
string description)
{ ... }
I'm under the uneducated impression that all of those arguments above will be populated by the forms collection values ... but it's not happening. Also, I have no idea how I would write a route, to handle those ... because those values are form post data....
here's my global.asax....
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Api - Search methods.
routes.MapRoute(
"Search Methods",
"{controller}/{action}"
);
In fact, the action method is never called because it doesn't seem to find it :(
But, if create and action without any of those arguments, then it finds it ?????????
How would you write a route and action method to accept some require and some optional arguments, for the route /review/create ?
As far as i can see you may rewrite your controller action like this:
public ActionResult Create(int foo, int bar, byte blah, string name, int? xxx) {
// code here
}
The ModelBinder will then ensure that foo,bar and blah are set. Name and xxx may be null. I can't test it a the moment, but i think return type of the action should be ActionResult.
If you are POST'ing a form, just make sure that the elements in your form (textboxes, checkboxes, textarea, etc) have id's that match the parameters in your method. As an alternative you can pass a FormCollection to the method, and do myFormCollection["foo"] to get a string representation of the value (which can then be parsed to an int).
From my experience, you are missing a number of key elements and concepts with this question.
First and foremost, I don't believe you can execute a POST without a form. The form has to contain the controls from which you pull the values that get passed to the controller method. If the goal is to simply unit test your POST controller method, then just call the method directly in your test, which it appears that you're doing, based on one of your comments. If you involve the view, then you're doing integration testing, not unit testing. Regardless of the test type, the test will always fail because you are choosing not to build the form. Even if you manage to force the POST using Fiddler, Firebug or any other mechanism, you're still not testing the view, you're testing the HTTP protocol.
I highly recommend that you employ a web application testing tool, such as WatiN or Selenium, to test your web pages, rather than throw together a quick-and-dirty test that really doesn't test anything useful.
In your post request set content-type="application/json; charset=UTF-8" and pass the values for the method parameter in JSON format. This should make Asp.MVC not to look in FormCollection for those values.

ASP.NET MVC model binding and action parameters

Let's say I have a controller action defined as:
public ActionResult(MyModel model, string someParameter)
{
// do stuff
}
I have a custom model binder for the MyModel type, and there is a form field called "SomeParameter" in the view that is not bound to the model. How does ASP.NET MVC know to pass the value of Request.Form["SomeParameter"] as the value for the "someParameter" argument to the action?
ASP.NET uses reflection to determine the correct method to invoke and to built up the parameters to pass. It does so based on the FormCollection array. Basically it will see model.* Keysin there and a FormCollection["someParameter"] it will first try Action(model,someParameter) then Action(model) and then Action(). Since it finds an Action with a model and someParameter arguments it will then try to convert them into the arguments types.
However by default it does so blindly which introduces some security risks, this blog post goes into greater detail on this.
If anyone can post up a link which in greater detail describes how ModelBinding is done under the hood that would be great.
Because the default model binder will be used for someParameter unless you specify otherwise. And the default model binder does exactly what you describe.
Phil Haack has a post on How a Method becomes an Action which explains just how this resolution happens.
Sounds like you need a model binder. These allow you to define how form data is bound to a model parameter. You can read more about them at the following:
ASP.NET MVC Preview 5 and Form Posting Scenarios
How to use the ASP.NET MVC Modelbinder
One of the easiest way is having Html items on the page with same name as the input parameters in the action method.
EX) In the View we have:
<input name="refNo" type="text">
Then in the Action method:
public ActionResult getOrders(string refNo)
So, it simply bind the value of "refNo" to the input parameter of the "getOrders" action.

Resources