2

I am doing a project where I am supposed to make an eBook store. This is an entity relationship model using which I generated a DB in SQL Server.

enter image description here

Now, while generating the bill using the following code, I am getting the An entity object cannot be referenced by multiple instances of IEntityChangeTracker. exception in while calling the SaveChanges() method for my Entity Relationship model, (ebs)

Here is the code. I am maintaining the cart in the session. Also, the user id is kept in the session too.

List<Title> cartItems = (List<Title>)Session["eStoreCart"];
int userid = Int32.Parse(Session["eStoreUserId"].ToString());
User us = ebs.Users.SingleOrDefault(u => u.UserId == userid);
Bill bl = new Bill();
bl.BillAmount = Decimal.Parse(lblBill.Text);
bl.BillDate = DateTime.Now;
foreach (Title item in cartItems)
{
    bl.Titles.Add(item);
}
us.Bills.Add(bl);
ebs.SaveChanges();
Response.Redirect("Orders.aspx");

I am totally new to Entity Framework and LINQ. So any help explaining what is going on, and a workaround will be appreciated.

pb2q
  • 58,613
  • 19
  • 146
  • 147
Rangan Das
  • 323
  • 2
  • 11

1 Answers1

2

It looks like you may be creating your DbContext ebs as an instance variable and keeping it around. You should instead consider your entity contexts lightweight and create them on demand, i.e. each time you need to query or modify the data store, especially since using this in a web application.

Please review these docs: Working with DbContext and Add/Attach and Entity States.

Specifically, note this:

When working with Web applications, use a context instance per request.

So don't keep a long-running DbContext around: instead create a new one in a using block when you want to modify your data store. DbContext is pretty lightweight and so you can build them and tear them down like this on demand.

See also this answer.

Now, as for the exception: I expect that it's due to the Title objects that you're storing in the session: those must have been created in another DbContext. You can try to attach them to the new DbContext (review the doc linked above). But another approach: don't keep the Title objects in the session, instead keep IDs for those objects, then look them up again in the new DbContext.

Something like this:

In your case, the code might look something like this:

List<int> cartItemIds = (List<int>)Session["eStoreCart"];
int userid = Int32.Parse(Session["eStoreUserId"].ToString());

using (var ebs = new MyDbContext())
{
    User us = ebs.Users.SingleOrDefault(u => u.UserId == userid);
    Title title = null;
    Bill bl = new Bill();

    bl.BillAmount = Decimal.Parse(lblBill.Text);
    bl.BillDate = DateTime.Now;

    foreach (var id in cartItems)
    {
        title = ebs.Titles.Where(t => t.Id == id);
        bl.Titles.Add(title);
    }

    us.Bills.Add(bl);
    ebs.SaveChanges();
}

Response.Redirect("Orders.aspx");

Another tip: look into the ASP Membership API for some additional webapp user support. You may not need to change much, and you'll get some API for doing things like password policies/expiration/changing.

Community
  • 1
  • 1
pb2q
  • 58,613
  • 19
  • 146
  • 147
  • I used this code, and the only change I made was ` using (eBookStoreDBModelContainer ebs = new eBookStoreDBModelContainer())` which actually gives the same error in the `ebs.SaveChanges();` line. – Rangan Das Jul 18 '15 at 16:01
  • Rangan: do you have another context that remains open? – pb2q Jul 18 '15 at 16:07
  • I think so. I have a globally declared `eBookStoreDBModel ebs` and on the `Page_Load`, I have `ebs = new eBookStoreDBModel`. I have used this only all throughout the page and it works alright in all pages, except this. – Rangan Das Jul 18 '15 at 16:09
  • Thanks for the help. I had to fiddle around a bit and this code works. ` foreach (Title item in cartItems) { bl.Titles.Add(ebs.Titles.SingleOrDefault(ttl => ttl.TitleId == item.TitleId)); } ` – Rangan Das Jul 18 '15 at 18:21