1

So I have the following 2 model:

[Table("Company")]
public class Company {
    public virtual List<UserAccount> Users {
        get {
            // I load my users here
        }
    }
}
[Table("UserAccount")]
public class UserAccount {
    public string email { get; set; }
}

In my view, I try to edit it:

@model MyXsite2013.Company
<table>
foreach (UserAccount ua in Model.Users) {
    <tr class="noRowHover">
        <td>
            @Html.TextBoxFor(modelItem => ua.email)
        </td>
    </tr>
}
</table>

And on postback, I try to save:

public ActionResult Edit(Company companyModel) {
    Company companyContext = database.Companies.Find(companyModel.ID);
    database.Entry(companyContext).CurrentValues.SetValues(companyModel);
    companyContext.IsActive = true;
    database.SaveChanges();
}

This of course, does not save the changes to the Users, in fact, it's not even seeing the changes coming in.

Bill Software Engineer
  • 7,362
  • 23
  • 91
  • 174
  • According to my understanding of your question. We cannot use in this passion because virtual keyword is used for foreign key purpose. if we use virtual keyword it means that collection will be lazy load. please correct me if I am wronge. – rishikesh tadaka May 08 '13 at 15:17
  • PLease read [this](http://stackoverflow.com/questions/4949991/binding-collections-in-mvc) and [this](http://stackoverflow.com/questions/6852559/mvc3-modelbinding-to-a-collection-posted-back-with-index-gaps). After successful bind in the post action you read Company then update it's properties and collection (merge\removeAll+add). – lavrik May 08 '13 at 15:20
  • You should wrap your form in `@using (Html.BeginForm()) { }`. – Henk Mollema May 08 '13 at 19:32

3 Answers3

1

Your problem is that you write your view in wrong way. If you want to bind model that contains list, you should set special names to them.

Something like:

@model MyXsite2013.Company
<table>
@{
   var i=0;
}
@foreach (UserAccount ua in Model.Users) {
    <tr class="noRowHover">
        <td>
            <input type="text" name="Model.Users[@i].email"/>
        </td>
    </tr>
    i++;
}
</table>

More information on this theme here

Update

You can use HtmlHelper methods:

@using(Html.BeginForm("Post", "Home", FormMethod.Post))
{
    int i = 0;
    foreach (var ua in Model.Users)
    {
        @Html.TextBox(string.Format("Model.Users[{0}].email", i), ua.email)
        i++;
    }
    <button type="submit">Submit</button>
}

And there should be public setter:

[Table("Company")]
public class Company {
    public virtual List<UserAccount> Users {
        get;
        set;
    }
}
Kirill Bestemyanov
  • 11,946
  • 2
  • 24
  • 38
0
  1. Your Company model's Users property needs a public setter. Without it, the model binder will not be able to create a new List instance to populate.

  2. You can't use a foreach loop in your view, because the html helpers won't render the correct tags needed for posting back properly. You need to use a regular for loop there instead.

rossisdead
  • 2,102
  • 1
  • 19
  • 30
0

I followed the answer provided by Kirill Bestemyanov, but made some modifications:

[Table("Company")]
public class Company {
    public virtual List<UserAccount> Users {
        get;
        set;
    }
}

And the View:

@model MyXsite2013.Company
<table>
@{
   var i=0;
}
@foreach (UserAccount ua in Model.Users) {
    <tr class="noRowHover">
        <td>
            <input type="text" name="Users[@i].email" value="@ua.email"/>
        </td>
    </tr>
    i++;
}
</table>

And the postback:

foreach (UserAccount ua in companyModel.Users) {
    UserAccount UA_Context = database.UserAccounts.Find(ua.ID);
    database.Entry(UA_Context).CurrentValues.SetValues(ua);
}
database.SaveChanges();
Bill Software Engineer
  • 7,362
  • 23
  • 91
  • 174