DbUpdateConcurrencyException when editing data that already exists even though I pass the variables needed

落花浮王杯 提交于 2019-12-25 07:00:31

问题


I'm getting the DbUpdateConcurrencyException error even though I am passing the OrderID to the OrderItem form. It works on Create and Delete, but it keeps kicking me out for Edit. Can anyone please suggest a fix or let me know if I'm doing something wrong?

I get this Concurrency Exception Error:

System.Data.Entity.Infrastructure.DbUpdateConcurrencyException was unhandled by user code HResult=-2146233087 Message=Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
Source=EntityFramework StackTrace: at System.Data.Entity.Internal.InternalContext.SaveChanges() at System.Data.Entity.Internal.LazyInternalContext.SaveChanges() at System.Data.Entity.DbContext.SaveChanges() at HealthHabitat.Controllers.OrderItemController.Edit(OrderItem orderItem) in c:\Users\Luffy\Desktop\HealthHabitat V25\HealthHabitat\Controllers\OrderItemController.cs:line 97 at lambda_method(Closure , ControllerBase , Object[] ) at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) at System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation.InvokeSynchronousActionMethod() at System.Web.Mvc.Async.AsyncControllerActionInvoker.b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult2.CallEndDelegate(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase1.End() at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag) at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.b__3d() at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.b__3f() InnerException: System.Data.Entity.Core.OptimisticConcurrencyException HResult=-2146233087 Message=Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions. Source=EntityFramework StackTrace: at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.ValidateRowsAffected(Int64 rowsAffected, UpdateCommand source) at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update() at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.b__2(UpdateTranslator ut) at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update[T](T noChangesResult, Func2 updateFunction) at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update() at System.Data.Entity.Core.Objects.ObjectContext.<SaveChangesToStore>b__35() at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess) at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction) at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass2a.b__27() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation) at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction) at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(SaveOptions options) at System.Data.Entity.Internal.InternalContext.SaveChanges() InnerException:

Controller:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "OrderItemID,OrderID,ItemID,Quantity")] OrderItem orderItem)
{
    if (ModelState.IsValid)
    {
        db.Entry(orderItem).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Details", "Order", new { id = orderItem.OrderID });
    }
    ViewBag.ItemID = new SelectList(db.Items, "ItemID", "Name", orderItem.ItemID);
    ViewBag.OrderID = new SelectList(db.Orders, "OrderID", "OrderID", orderItem.OrderID);
    return View(orderItem);
}

Edit View:

@using (Html.BeginForm(new { OrderID = Model.OrderID }))
{
    @Html.AntiForgeryToken()
    <div class="panel panel-warning">
        <div class="panel-heading">
        <h4><i class="fa fa-edit"></i> Edit</h4>
        </div>
        <div class="panel-body">
            <div class="form-horizontal">
                <hr />
                @Html.ValidationSummary(true, "", new { @class = "text-danger" })
               @Html.HiddenFor(model => model.OrderID)

                <div class="form-group">
                    @Html.LabelFor(model => model.ItemID, "Item Name", htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.DropDownList("ItemID", null, htmlAttributes: new { @class = "form-control"})
                        @Html.ValidationMessageFor(model => model.ItemID, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    @Html.LabelFor(model => model.Quantity, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Quantity, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Quantity, "", new { @class = "text-danger" })
                    </div>
                </div>
                <hr />
                    <div class="form-group">
                        <div class="col-md-offset-2 col-md-10">
                            <a href="@Url.Action("Details", "Order", new { id = Model.OrderID }, null)" class="btn btn-default">Cancel</a>
                            <input type="submit" value="Save" class="btn btn-warning"/>
                        </div>
                    </div>
                </div>
            </div>
        </div>
            }

Model:

public class OrderItem
{

    public int OrderItemID { get; set; }
    public int OrderID { get; set; }
    public int ItemID { get; set; }

   [Range(1, 30, ErrorMessage = "{0} must be between {1} and {2}.")] // is this a row version?? I'm not sure, I'm kinda new to MVC
    public int Quantity { get; set; }

    public virtual Order Order { get; set; }
    public virtual Item Item { get; set; }

}

GET Edit Method

// GET: OrderItem/Edit/5
public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    OrderItem orderItem = db.OrderItems.Find(id);
    if (orderItem == null)
    {
        return HttpNotFound();
    }

    ViewBag.ItemID = new SelectList(db.Items, "ItemID", "Name", orderItem.ItemID);
    ViewBag.OrderID = new SelectList(db.Orders, "OrderID", "OrderID", orderItem.OrderID);
    return View(orderItem);
}

回答1:


You have to put the RowVersion property on the view, and send it to the controller:

@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.OrderID)
@Html.HiddenFor(model => model.RowVersion)

Controller

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "OrderItemID,OrderID,ItemID,Quantity,RowVersion")] OrderItem orderItem)
{
    if (ModelState.IsValid)
    {
        db.Entry(orderItem).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Details", "Order", new { id = orderItem.OrderID });
    }
    ViewBag.ItemID = new SelectList(db.Items, "ItemID", "Name", orderItem.ItemID);
    ViewBag.OrderID = new SelectList(db.Orders, "OrderID", "OrderID", orderItem.OrderID);
    return View(orderItem);
}

Be aware that properties which were not modified on view will be empty. It is better to specify the modified properties.

//db.Entry(orderItem).State = EntityState.Modified;
db.Entry(orderItem).Property(i => i.Quantity).IsModified = true;
//...

EDIT

Just for testing purposes, instead of

@using (Html.BeginForm(new { OrderID = Model.OrderID }))

Try

@using (Html.BeginForm())
{
   @Html.HiddenFor(i => i.OrderID)
}

EDIT 2

Change your GET Action for:

// GET: OrderItem/Edit/5
public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    OrderItem orderItem = db.OrderItems.Find(id);
    if (orderItem == null)
    {
        return HttpNotFound();
    }

    ViewBag.DDL_ItemID = db.Items.Select(i => new SelectListItem { Text = i.Name, Value = i.ItemID.ToString() }).ToList();
    ViewBag.DDL_OrderID = db.Orders.Select(i => new SelectListItem { Text = i.OrderID.ToString(), Value = i.OrderID.ToString() }).ToList();
    return View(orderItem);
}

Do not ever use a ViewBag property with the same name of a Model property

Change the DropDownList in your view:

@Html.DropDownListFor(i => i.ItemID, (IEnumerable<SelectListItem>)ViewBag.DDL_ItemID, new { @class = "form-control"})

In the view, where did you put the OrderItemID? You have to put it there.



来源:https://stackoverflow.com/questions/32405011/dbupdateconcurrencyexception-when-editing-data-that-already-exists-even-though-i

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!