ASP.Net MVC - model with collection not populating

2020-01-28 09:43发布

I have an ASP.Net MVC application with a model which is several layers deep containing a collection.

I believe that the view to create the objects is all set up correctly, but it just does not populate the collection within the model when I post the form to the server.

I have a piece of data which is found in the class hierarchy thus:

person.PersonDetails.ContactInformation[0].Data;

This class structure is created by LinqToSQL, and ContactInformation is of type EntitySet<ContactData>. To create the view I pass the following:

return View(person);

and within the view I have a form which contains a single text box with a name associated to the above mentioned field:

<%= Html.TextBox("person.PersonDetails.ContactInformation[0].Data")%>

The post method within my controller is then as follows:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create (Person person)
{
  //Do stuff to validate and add to the database 
}

It is at this point where I get lost as person.PersonDetails.ContactInformation.Count() ==0. So the ModelBinder has created a ContactInformation object but not populated it with the object which it should hold (i.e ContactData) at index 0.

My question is two fold: 1. Have I taken the correct approach.. i.e. should this work? 2. Any ideas as to why it might be failing to populate the ContactInformation object?

Many thanks, Richard

6条回答
相关推荐>>
2楼-- · 2020-01-28 09:55

The first argument of Html.TextBox is the name of the textbox, the second would be the value.

"Wrong":

<%= Html.TextBox("person.PersonDetails.ContactInformation[0].Data")%>

"Right":

<%= Html.TextBox("nameoftextbox", person.PersonDetails.ContactInformation[0].Data)%>
查看更多
家丑人穷心不美
3楼-- · 2020-01-28 09:58

Make sure your models (and all nested models) are using properties (getters/setters) instead of fields. Apparently the default binder needs properties to function properly. I had a very similar situation that was fixed by changing the necessary fields to properties.

查看更多
劫难
4楼-- · 2020-01-28 10:10

Maybe lack of Bind attribute is the case:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create ([Bind] Person person)
{
// Do stuff to validate and add to the database 
}
查看更多
Melony?
5楼-- · 2020-01-28 10:11

I think that your model is too complex for the default model binder to work with. You could try using multiple parameters and binding them with prefixes:

public ActionResult Create( 
    Person person,
    [Bind(Prefix="Person.PersonDetails")]
    PersonDetails details,
    [Bind(Prefix="Person.PersonDetails.ContactInformation")] 
    ContactInformation[] info )
{
      person.PersonDetails = details;
      person.PersonDetails.ContactInformation = info;

      ...
}

Or you could develop your own custom model binder that would understand how to derive your complex model from the form inputs.

查看更多
我欲成王,谁敢阻挡
6楼-- · 2020-01-28 10:11

I've been struggling with this same type of scenario and eventually came to realize that the underlying problem is that the MVC default model binder does not seem to work on EntitySet<T> fields, only List<T> fields. I did however find a simple workaround that seems acceptable. In my case, I have a Company entity that has one to many relationship to Contacts (my Linq-to-Sql EntitySet).

Since it seems that when I change my code from EntitySet<Contact> to List<Contact>, the MVC default model binder starts working as expected (even though the LTS isn't now), I figured I would provide an alternate, "aliased" property to MVC that is of type List<Contact>, and sure enough, this seems to work.

In my Company entity class:


// This is what LINQ-to-SQL will use:
private EntitySet<Contact> _Contacts = new EntitySet<Contact>();
[Association(Storage="_Contacts", OtherKey="CompanyID", ThisKey="ID")]
public EntitySet<Contact> Contacts
{
    get { return _Contacts; }
    set { _Contacts.Assign(value); }
}

// This is what MVC default model binder (and my View) will use:
public List<Contact> MvcContacts
{
    get { return _Contacts.ToList<Contact>(); }
    set { _Contacts.AddRange(value); }
}

So now, in my View, I have the following:

<label>First Name*
    <%= Html.TextBox("Company.MvcContacts[" + i + "].FirstName") %>
</label>
<label>Last Name*
    <%= Html.TextBox("Company.MvcContacts[" + i + "].LastName") %>
</label>

Seems to work like a charm!

Best of luck! -Mike

查看更多
贪生不怕死
7楼-- · 2020-01-28 10:12

If a property is null, then the model binder other could not find it or could not find values in the submitted form necessary to make an instance of the type of the property. For example, if the property has a non-nullable ID and your form does not contain any data for that ID , the model binder will leave the property as null since it cannot make a new instance of the type without knowing the ID.

In other words, to diagnose this problem you must carefully compare the data in the submitted form (this is easy to see with Firebug or Fiddler) with the structure of the object you are expecting the model binder to populate. If any required fields are missing, or if the values are submitted in such a way that they cannot be converted to the type of a required field, then the entire object will be left null.

查看更多
登录 后发表回答