How does Controller get the Model from the View FormMethod.Post? - asp.net-mvc

In my ASP.NET MVC 5.2 application running .NET Framework v4.5.2, my AdminController has an InventoryQueryList method that accepts the model:
[HandleError]
[HttpPost]
public ActionResult InventoryQueryList(CheckInventoryQueryModel model)
{
// ...
}
My view contains the model and calls the InventoryQueryList method on POST:
#model CCNRebuild.Models.CheckInventoryQueryModel
#{
ViewBag.Title = "InventoryQuery";
Layout = "~/Views/Shared/_ConsoleLayout.cshtml";
}
#using (Html.BeginForm("InventoryQueryList", "Admin", FormMethod.Post))
{
#Html.AntiForgeryToken();
<label>
Dealer:#Html.DropDownListFor(m => m.DealerID, Model.Dealerships)
</label>
...
<input type="submit" value="Submit" />
}
But, whenever I click the submit button, I get an error:
MissingMethodException: No parameterless constructor defined for this object.
Why is the view not passing my model parameter to the controller?
The controller has never had a parameterless constructor in the 3 months that I have been working here.
NOTE: I tried to only show relevant code, and I left out anything unnecessary. If there is anything missing that needs to be seen, please comment and I'll try to oblige.

The error is telling you that CheckInventoryQueryModel doesn't have a parameterless constructor, and it needs one. So you would either:
Remove whatever non-parameterless constructor(s) it does have (and update related code accordingly), or
Add a parameterless constructor.
The model binder needs a parameterless constructor in order to construct an instance of the model. (Unless you write a custom model binder for this type. Which probably isn't the road you want to take, but is an option.) This is a fairly common pattern in frameworks that automate model instance creation. Entity Framework, for example.
As for the actual questions being asked...
How does Controller get the Model from the View FormMethod.Post?
and
Why is the view not passing my model parameter to the controller?
It just sounds like you were misinterpreting the error. I see no reason to suggest that the page isn't passing the form value(s) to the server. Though you can always confirm this in your browser's debugging tools by observing the HTTP request being made when posting the form.
The request sends the data to the server, and can do so in a variety of ways. Form posts are generally key/value pairs. The ASP.NET MVC framework's model binder uses those key/value pairs to populate properties on the model. It just couldn't do that in this case because it couldn't create an instance of the model to be populated.

Related

MVC Create method data flow

I think i know some of the basics of MVC but there's one thing which I don't understand yet.
In the view Create which is generated automatically when you set up your project, how is data sent to the controller? I'm used to seeing ActionLinks with parameters but here there's no actionLink so I can't understand how data travel from the view to the controller.
Could you explain it to me please?
as you know, in your view, the very first line (usually) tells the view, about the Model being used within this view. like:
#model Models.CarViewModel
lets suppose, you have a form on this view, and it is posted to some action called Edit. Then you must have your Edit action, expecting the parameter of type you used as model in your view. like:
[HttpPost]
public ActionResult(CarViewModel model)
{
//logic
}
This convention is known as Strongly Typed View. Suppose Then you have some textbox for a property Name of your model as:
#Html.TextBoxFor(x => x.Name)
when the form is posted to Edit Action, the variable model in parameter of Edit action will be holding the respective values. i.e, model.Name

Model usage within View

I think I'm not clear with the #model that can be part of the view
For example
#model MyModel
Is it the input argument that I can populate and call the view with?
return View("MyView", MyModel);
Is it the output variable I can populate during the post of the view (for next control action)
[HttpPost]
public ActionResult SomePostAction(MyModel myModel) //(and in post action)
Is it both ??
3, it's both!
It is called model binding. A feature of ASP.NET which makes it trivial to bind a mode to a view. Hence the name 'view model', which those models are usually called.
Assigning a model to your view gives you a so-called strongly typed view, which fully exposes the power over the Razor syntax.
The model binder is capable of binding the values of every input field back to the model when posting the form, as long as the name attribute of the form matches the name of the property on the view model. Html helpers such as Html.EditorFor(m => m.SomeProperty) makes this a trivial task.
As Mystere Man mentions, it's also possible to do this without an actual model in your view. For instance, this works:
Html (I omitted the form tag and submit button):
<input type="text" name="SomeString" />
with this method in your controller:
[HttpPost]
public ActionResult SomeAction(string someString)
{
// ...
}
The #model declaration at the top of your view is related to the model object you passed to the View() method in your controller (option 1 in your question). The #model declaration is your way of telling the Razor view engine that the view is strongly typed. That means the C# compiler can double check any properties of your view accesses.
Suppose you had the following class
public class MyModel
{
public string Name { get; set; }
}
Without a strongly typed view you might have something like this in your view
<div>
Hello, #Model.Nmae
</div>
Notice that there's a typo in Name. ASP.Net has no idea what your model is so it has to use a dynamic object. You won't find that error until runtime. If you had delcared #model MyModel you would have an error at build time because MyModel doesn't have a Nmae property.
However, it's not uncommon to use the same model type as a parameter of the action. Imagine your page is an HTML form. In that case the model that your view is strongly typed to and the model that's passed to an MVC action could be the same.

Some clarity on how to use View Models with MVC and ModelBinding please

I am getting confused about the use of ViewModels in an edit form where one of the properties is the editable entity ie
ViewModel
Entity
Actions
The model desclaration at the top of the View page:
#model MyProject.Models.ViewModel
Not
#model MyProject.Models.Entity
So what is the best way to represent the Entity Property which is the one that needs updating. I first started making a variable out of it.
Entity myEntity = ViewModel.Entity;
So each form element edit field might be represented by:
#Html.EditorFor(model => myEntity.Name)
However what happens with the Model Binding parameters in the post action?
[HttpPost]
public ActionResult Edit(Entity myEntity)
db.Entities.Attach(myEntity);
db.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
db.SaveChanges();
So this question is really about how to deal with editable property objects in a ViewModel as opposed to a specific Entity Object that could be passed in as the Model which is straight forward.
Answer most likely simple.
Huge thanks in advance.
EDIT:
Feedback on how to reference domain objects from a ViewModel in a View. I have found that if I reference them directly in the lambda expresions, then model binding works fine as it can use the derived ids to navigate around the returned ViewModel. If one use a local variable in the View then this variable name is then used in the Id which then breaks the ViewModel Model binding mapping.
Your POST action should take the view model as parameter, not the domain model:
[HttpPost]
public ActionResult Edit(ViewModel viewModel)
then you should use the ID of this viewModel to retrieve the corresponding domain model from the database that needs to be updated. And then update the properties of this domain model from the view model. Finally persist the domain model back to the database.

MVC2 Post using sub-classes

I'm building one page to edit various product types. Each product type has a view model (TentProductVM, BootProductVM) that inherits from ProductVM. My MVC2 View checks the model type and adds fields as appropriate. For example, if the model is of type BootProductVM I call Html.TextBoxFor to add a field for the boot's foot size. Page displays fine.
The problem is the post. I've declared a function (in VB) as follows:
<HttpPost()>Function Edit(byval prod as ProductVM) As ActionResult
Of course, this function only receives the form data from the base class ProductVM. So instead I added a function for each product type:
<HttpPost()>Function EditTent(byval prod as TentProductVM) As ActionResult
<HttpPost()>Function EditBoot(byval prod as BootProductVM) As ActionResult
and point the View to the appropriate post function:
Using Html.BeginForm("Edit" & Model.ObjectTypeName, "Catalog")
However, when EditTent or EditBoot gets called, the prod parameter only contains data from the base class. All the fields declared in the subclass view models are left at default values.
What am I doing wrong? Or is there a better approach? (The obvious solution is multiple pages but, since ProductVM has many fields relative to the subclasses, I'd rather not.)
After much experimentation, I've decided not to use this approach. First, I couldn't get it to work without resorting to having an Action parameter of type FormCollection. Second, the obvious solution I discarded is appealing if I use a partial view. The partial view has all the fields associated with the base class (ProductVM), leaving only the fields associated with the derived classes (TentProductVM, BootProductVM) in the regular views.
Felt like I was fighting against the MVC auto-magic, which is never the right approach.
The thing to remember about MVC is that it's based on the "Convention over Configuration" mindset. So if you're passing a strongly typed class class instance to your action method, it expects it to be named "model".
Try changing your declarations to look like this:
<HttpPost()> Function EditTent(byval model as TentProductVM) As ActionResult
<HttpPost()> Function EditBoot(byval model as BootProductVM) As ActionResult
The other (less ideal) option would be to expect a FormCollection object in your action method.
<HttpPost()> Function EditTent(byval form as FormCollection) as ActionResult
Update
Just updating to include some of the discussion points below... In order to post a strongly typed object to a controller action method, the types need to match up.
Assuming your controller's action method looks like this:
<HttpPost()> Function EditTent(byval model as ProductVM) As ActionResult
Your view should be typed accordingly:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Your.Namespace.ProductVM>" %>

Posting multiple values using MVC

I have a model with a property that points to a file that contains HTML. My strongly typed view to this model uses a custom HTML helper method to resolve and return the HTML from the file. Works great so far.
The HTML read from each file will contain various controls whose values I need to retrieve when the form is POSTed.
What would be the best way to have access to the POSTed control values in my controller method?
I would prefer a non jQuery solution, but I am not sure if the MVC framework can provide these values to me? Can it provide a list of key/value pairs to the controller somehow?
You could use the FormCollection in ASP.NET MVC.
public ActionResult SomeAction(FormCollection form) {
...
}
You have essentially two options.
1) Use the old fashioned Request variables as all we have done in ASP.NET web forms.
For example in your controller action method you can retrieve any value present on the form with the following method
public ActionResult SomeAction() {
var request = this.ControllerContext.HttpContext.Request;
bool boolParam = bool.Parse( request["boolParam"] ?? "false" );
}
2) Create a custom Model Binder to let the framework pack those values in a custom class object.
This method would be a little bit more difficult at the beginning because you have to create a custom Model Binder but it favour readability on your controller code. For further details on creating custom model binders give a look at the following links (you can find more with a simple search)
Custom Model Binder for Complex composite objects
Custom Model Binder and More UI Validation in ASP.NET MVC
A Custom ASP.NET MVC Model Binder for Repositories
Hope it helps
Is the content of the HTML files dynamic or known at design time? If you know it now, you could have each one post to it's own action and then strongly type the parameters.

Resources