修改实体状态时发生错误

时间:2014-06-22 20:07:26

标签: c# asp.net-mvc entity-framework

我正在尝试使用购物车测试WCF Web服务。下面的方法基本上接收CartLine列表,并从数据库中减去添加到购物车的所有产品。然后,它会发送一条消息,或者如果库存不足,则发送订单取消消息。

public string Deliver(List<CartLine> cartLine)
        {
            string strOut = null;
            StartPurchase();

            if (Convert.ToBoolean(HttpContext.Current.Session["TransactionStarted"]))
            {
                //Traverse the cart line
                for (int i = 0; i < cartLine.Count; i++)
                {
                    //for each product in the cart line decrease inventory
                    if (cartLine[i].Product.Stock > 0)
                    {
                        //here decreasing inventory
                        (cartLine[i].Product.Stock) -= (cartLine[i].Quantity);

                             //Advice that entity has changed
                            db.Entry(cartLine[i].Product).State = EntityState.Modified; //offending line
                            db.SaveChanges();

                       strOut = "Order Processed!";
                    }
                    else
                    {
                       strOut =  "Order cancelled, Stock missing!";

                    }
                }

               return strOut;
            }
            else
            {
                return m_cartSessionNotStartedStr;
            }
        }

如果我将相同类型的产品添加到购物车,一切正常。然而,只要我混合产品,我就会收到此错误:

附加类型&#39; X'的实体失败,因为同一类型的另一个实体已经具有相同的主键值。使用&#39;附加&#39;方法或将实体的状态设置为“未更改”#39;或者&#39;修改&#39;如果图中的任何实体具有冲突的键值。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,请使用&#39;添加&#39;方法或“添加”#39;实体状态跟踪图形,然后将非新实体的状态设置为“未更改”。或者&#39;修改&#39;视情况而定。

保存更改时会发生此错误(请参阅上面的违规行)。虽然我理解我应该附加该实体,但我不知道如果它是产品系列列表的话,该怎么办。

有人可以帮忙吗?

谢谢

4 个答案:

答案 0 :(得分:4)

您必须检查具有相同密钥的实体是否已被上下文跟踪。如果跟踪了实体,请修改该实体,而不是附加当前实体:

var trackedEntity = context.Products.Find(cartLine[i].Product.Id);
context.Entry(trackedEntity).CurrentValues.SetValues(cartLine[i].Product);
context.Entry(trackedEntity).State = EntityState.Modified;
context.SaveChanges();

答案 1 :(得分:2)

您可以先将产品加载到上下文中。这样,实体将被修改而不被添加。

答案 2 :(得分:1)

我很确定你的问题是对象图中存在具有相同主键但引用不同的对象。

答案 3 :(得分:1)

正如brumScouse正确说的那样,您应该在修改产品之前从数据库加载产品。但不仅因为在这种情况下,您正在修改附加产品(因此您不必再自己管理其状态),而且还要确保您正在修改新鲜数量。在将产品提取到视图并最终减少数据库中的数量之间可能有相当长的时间。并发用户可能在两者之间做了同样的事情。

我会按如下方式获取产品:

// Fetch the products in the cartLines in one query
var productIds = cartLine.Select(c => c.Product.ProductId).ToList();
var dbProducts = db.Products.Where(p => productIds.Contains(p.ProductId));

然后在你的循环中:

var dbProduct = db.Products.Find(cartLine[i].Product.ProductId);
if (dbProduct.Stock > 0)
    dbProduct.Stock -= cartLine[i].Quantity;

Find将从本地收藏中获得Product

重要的一点是:确保实现乐观并发,以确保在从数据库中获取并修改和保存的少量时间内,不会有两个并发用户减少相同的Product库存