ASP.NET MVC3 and Entity Framework v4.1 with error

2019-04-12 17:51发布

问题:

Currently when adding a product to my cart the Add action of my CartController is called with the orderEntryDisplayViewModel (the order line object).

    [HttpPost]
    public RedirectToRouteResult Add(Cart cart, OrderEntryDisplayViewModel orderLine)
    {
        if (!ModelState.IsValid)
        { return RedirectToAction("Display", "OrderEntry", new { Product = orderLine.Line.PartNum }); }
        CompleteProduct product = null;
        orderLine.Line.RequestedShipDate = orderLine.RequestedShipDate;
        if (orderLine.Line.NewMyPartNum != null)
        { orderLine.Line.MyPartNum = orderLine.Line.NewMyPartNum; }
        try
        {
            product = _inventoryRepo.FetchByPartNum(orderLine.Line.PartNum, User.Identity.Name);
            orderLine.Line.Product = product;
            cart.AddItem(orderLine.Line);
            //_cartRepo.Save();
        }
        catch (DbUpdateException e)
        { throw; }
        catch
        {
            ModelState.AddModelError("", "Problem adding part to cart");
            return RedirectToAction("Index", new { returnUrl = Url.Action("Index", "OrderEntry") });
        }
        return RedirectToAction("Index", new { returnUrl = Url.Action("Index", "OrderEntry") });
    }

Before it is reached the CartModelBinder either creates or gets the shopping cart from the session.

public class CartModelBinder : IModelBinder
{
    private const string sessionKey = "Cart";

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        CartRepository cartRepo = new CartRepository();
        Cart cart = (Cart)controllerContext.HttpContext.Session[sessionKey];
        if (cart == null)
        {
            cart = cartRepo.CreateCart(true);
            cartRepo.DetachCart(cart);
            controllerContext.HttpContext.Session[sessionKey] = cart;
            cartRepo.AttachCart(cart);
        }
        else
        { cartRepo.AttachCart(cart); }
        return cart;
    }
}

If no cart currently exists in the session a new one is created though the CartRepository CreateCart method which then adds the newly created cart to the context.

    public Cart CreateCart(bool saveCart = false)
    {
        Cart cart = new Cart();
        context.Carts.Add(cart);
        if (saveCart)
        { context.SaveChanges(); }
        return cart;
    }

Before I add the cart object to the session I detach it from the context using CartRepository DetachCart, add it to the session, then attach it to the context again.

public void DetachCart(Cart cart)
{
    ((IObjectContextAdapter)context).ObjectContext.Detach(cart);
}

The CartRepository gets the context from ContextHelper:

public static class ContextHelper
{
    public static InsideIIMAKContext InsideIIMAK
    {
        get
        {
            if (HttpContext.Current.Items["InsideIIMAKContext"] == null)
            { HttpContext.Current.Items.Add("InsideIIMAKContext", new InsideIIMAKContext()); }
            return (InsideIIMAKContext)HttpContext.Current.Items["InsideIIMAKContext"];
        }
    }

If CartModelBinder finds a cart in the session then it attempts to attach the cart through the CartRepsitory method AttachCart.

    public void AttachCart(Cart cart)
    {
        context.Carts.Attach(cart);
    }

At the end of the Add action in the CartController I redirect to the Index action to display the cart view. The index action requires the Cart object too so CartModelBinder is called a second time and this time calls the CartRepository AttachCart method. My error occurs at the AttachCart method

"An entity object cannot be referenced by multiple instances of IEntityChangeTracker."

I've researched this issue a good amount and it doesn't seem that I have a situation where I am adding the cart object to two instances of the context like seems to often be the reason for the error. It almost seems as if the cart object stored in the session is being modified with tracking information so when I try to attach it in the next HTTP request EF thinks it is attached to an active context, but really it's the context from the previous request. I found a forum which suggested I needed to detach the Entity Object before adding it to the session, but I've done that and still have the same issue.

Hopefully I haven't missed anything in this explanation.

ANSWER Thanks to Mark Oreta

    public void DetachCart(Cart cart)
    {
        var objectContext = ((IObjectContextAdapter)context).ObjectContext;
        objectContext.Detach(cart.Customer);
        foreach (var item in cart.Lines)
        { objectContext.Detach(item); }
        objectContext.Detach(cart);
    }

回答1:

When you're putting the cart into the session, are you detaching the object from that context? Entity Objects will carry the context with them, so you'll need to explicitly detach it prior to adding it.