locked
type 'CreditCard' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. RRS feed

  • Question

  • User-1181774692 posted

    This is what I receive as an exception:

    The instance of entity type 'CreditCard' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.

    So, the case is this: I am testing a method that if returns a Transaction DTO correctly after passed valid parameters. And throughout the method I am calling some methods so I mocked the other services calling those methods and I did setup on them. I also created an User, Wallet and CreditCard (of course test ones) and I add them to the scoped database.

    After I get into my system under test, when I reach to making the transaction I successfully make the transaction and before passing it to another class to take it as a DTO, I first add the transaction to the context. And that is where the exception is thrown.

    !One thing to mind => I saw that after the exception was thrown, the context holds my credit card 2 times! The same property values, with the same id. That is why the exception was throwns I suppose. So my question here , to summarize it all is => how should one proceed? Since my business logic works after I start the program but in this current test method case this is what happens... 

    UPDATE: This is how it worked => either I use the same instance of the context with the same contextOptions like this :

    //Act
    using (var actContext = new ItsAllAboutTheGameDbContext(contextOptions))
    {
    await actContext.Users.AddAsync(user);
    await actContext.Wallets.AddAsync(userWallet);
    await actContext.CreditCards.AddAsync(creditCard);
    await actContext.SaveChangesAsync();
    var sut = new TransactionService(actContext, walletServiceMock.Object,
    userServiceMock.Object, foreignExchangeServiceMock.Object, cardServiceMock.Object, dateTimeProvider);
    var transactionDTO = await sut.MakeDeposit(user, creditCard.Id, amount);

    Assert.IsInstanceOfType(transactionDTO, typeof(TransactionDTO));
    }

    Or I just put this Update on the creditCard:

    //Assert
    using (var assertContext = new ItsAllAboutTheGameDbContext(contextOptions))
    {
    var sut = new TransactionService(assertContext, walletServiceMock.Object,
    userServiceMock.Object, foreignExchangeServiceMock.Object, cardServiceMock.Object, dateTimeProvider);
    assertContext.CreditCards.Update(creditCard); This is where I put this line and the exception was not thrown => only 1 card is added to the context
    var transactionDTO = await sut.MakeDeposit(user, creditCard.Id, amount);

    Assert.IsInstanceOfType(transactionDTO, typeof(TransactionDTO));
    }

    I want some answers :(

    Saturday, December 15, 2018 5:20 PM

Answers

  • User1520731567 posted

    Hi Edwardcho,

    !One thing to mind => I saw that after the exception was thrown, the context holds my credit card 2 times! The same property values, with the same id. That is why the exception was throwns I suppose.

    Your DB Context is being shared by multiple requests, meaning that the entity you're editing has been tracked already.

    That means that a new instance will be created for each request. Likewise, you will also have a per-request DB Context.

    For example:

       CreditCard objmodel = new CreditCards();
                objmodel.address1 = model.xxx1;
                objmodel.city = model.xxx2;
                ...
    
                actContext.Update(objmodel);
                actContext.SaveChanges();

    or try to use AsNoTracking() to 

    var objmodel = actContext.CreditCards.AsNoTracking().Where(t => t.Id== id).FirstOrDefault();
    actContext.Update(objmodel);
    ...

    or use EntityState.Detached,code like below:

    var local = _context.Set<CreditCards>()
        .Local
        .FirstOrDefault(entry => entry.Id.Equals(entryId));
    
    // check if local is not null 
    if (!local.IsNull()) // I'm using a extension method
    {
        // detach
        actContext.Entry(local).State = EntityState.Detached;
    }
    // set Modified flag in your entry
    actContext.Entry(entryToUpdate).State = EntityState.Modified;
    
    // save 
    actContext.SaveChanges();

    More details and methods,you could refer to:

    https://stackoverflow.com/a/42475617

    https://stackoverflow.com/a/48132201

    Hope my answer will helpful to you.

    Best Regards.

    Yuki Tao

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, December 17, 2018 8:30 AM