I have the Action method and the View for editing properties of some items.
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Admin")]
public async Task<ActionResult> Edit(Item item)
{
if (ModelState.IsValid)
{
db.Entry(item).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.CatagorieId = new SelectList(db.Catagories, "ID", "Name", item.CatagorieId);
return View(item);
}
and
@model OpenOrderFramework.Models.Item
@using OpenOrderFramework.Extensions
@{
ViewBag.Title = "edit";
}
<h2>Editing</h2>
@using (Html.BeginForm())
{
<div class="form-horizontal">
<h4>The car</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.ID)
<-- etc -->
But when I submit the form I get an error
Store update, insert, or delete statement affected an unexpected number of rows (0).
I figured out that in action method ID of the item that was posted is always 0 even if real ID of the item is different. Why does it happen?
GET Action method:
// GET: Items/Edit/5
[Authorize(Roles = "Admin")]
public async Task<ActionResult> Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Item item = await db.Items.FindAsync(id);
if (item == null)
{
return HttpNotFound();
}
ViewBag.CatagorieId = new SelectList(db.Catagories, "ID", "Name", item.CatagorieId);
return View(item);
}
When you post the form, the http call to your HttpPost action method is a totally separate Http request and Entity framework cannot track that entity.
As Darin mentioned in the comment, It is not a good idea to mix the entity classes in your UI layer. That makes it very tightly coupled.
What you should be using is creating and using a view model for your view. View model's are simply POCO classes, which is specific to the view.
public class ItemViewModel
{
public int Id {set;get;}
public string Name {set;get;}
public List<SelectListItem> Categories { set;get;}
public int SelectedCategory {set;get;}
}
And in your GET action, read the entity from your database,create an object of the view model and set the property values to that
public ActionResult Edit(int id)
{
var vm=new ItemViewModel { Id=id };
var item = db.Items.FirstOrDefault(s=>s.Id==id);
if(item!=null)
{
vm.Name = item.Name;
}
vm.Categories =db.Categories.Select(s=> new SelectListItem { Value=s.Id.ToString(),
Text=s.Name
}).ToList();
return View(vm);
}
And your view will be strongly typed to your view model
@model ItemViewModel
@using(Html.BeginForm())
{
@Html.DropdDownListFor(s=>s.SelectedCategory,Model.Categories,"Select")
@Html.HiddenFor(s=>s.Id)
@Html.TextBoxFor(s=>s.Name)
<input type="submit" />
}
And in your HttpPost action, read the existing entity from your db and update the property values you want to update.
[HttpPost]
public ActionResult Edit(ItemViewModel model)
{
if(ModelState.IsValid)
{
var item = d.Items.FirstOrDefault(s=>s.Id==model.Id);
item.Name = model.Name;
db.Entry(item).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
model.Categories =db.Categories.Select(s=>
new SelectListItem {
Value=s.Id.ToString(),
Text=s.Name }).ToList();
return View(model);
}
Make sure to add enough NULL checkings before accessing the entities/ objects in the code.