Reputation: 953
I have a Code First project MVC 5. Simplified I have the following model:
public class Person
{
public int Id { get; set; }
public string ItemCode { get; set; }
public string Description { get; set; }
}
In my view I have a table with textboxes:
@model IList<ItemApp.Models.Item>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
@using(Html.BeginForm("Process", "Items", FormMethod.Post))
{
@Html.AntiForgeryToken()
<button type="button" class="btn btn-primary" id="btnAddRow">AddRow</button>
<input type="submit" class="btn btn-success" value="process"/>
<table class="table">
<tr>
<th>
ItemCode
</th>
<th>
Description
</th>
</tr>
@for (int i = 0; i < Model.Count(); i++ )
{
<tr>
<td>
<input type="hidden" value="@i" name="Index" />
@Html.TextBoxFor(item => item[i].ItemCode)
</td>
<td>
@Html.TextBoxFor(item => item[i].Description)
</td>
</tr>
}
<tr>
<td>
<input type="hidden" value="4" name="Index" />
<input type="hidden" name="[4].Id" />
<input type="text" name="[4].ItemCode" />
</td>
<td>
<input type="text" name="[4].Description" />
</td>
</tr>
</table>
}
As you can see in my view, I've added an extra row. The first 3 rows are populated via the database when running te project. When I enter values in the last row and submit the form, you can see that my action receives a list of Item objects (Count = 4). My process action looks like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Process(List<Item> items)
{
if(ModelState.IsValid)
{
foreach(var item in items)
{
if(item.Id != 0)
{
db.Entry(item).State = System.Data.Entity.EntityState.Modified;
}
else
{
db.Items.Add(item);
}
db.SaveChanges();
}
return RedirectToAction("Index");
}
return RedirectToAction("Index");
}
The problem is that my ModelState.IsValid returns false. This is because of the Id = 0 for the last (newly entered) row. What I want is to submit the form, update all items that are already in the database and add a new item.
Upvotes: 0
Views: 1889
Reputation:
Your ModelState
is not invalid because Id=0
(zero is a valid value for int
) Its invalid because you post back null
for the 4th item. You need to change your view to
<input type="hidden" name="[4].Id" value="0"/>
so it posts back Id=0
(or you could simply omit the element and the value will be 0
when you submit the form, because that's the default value for int
)
However, since you do not generate a form control for the Id
property of each existing item, then those will also have Id=0
which means you will be adding the existing items again to the database, not updating them. Inside the loop you also need
@Html.HiddenFor(item => item[i].Id)
However you code is inflexible and will only ever work if you have exactly 3 existing items, and only ever allows you to add one new item. Suggest you review the answers here and here for dynamically (and deleting) items in a collection.
Upvotes: 2