I get an exception when I try to update graph. I use Automapper to map DTOs to my entities, then I check if my object already exists in the database before I do the update operation.
When I call dbContext.Update
, I get this exception
The instance of entity type 'Order' cannot be tracked because another instance with the same key value for {'OrderId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using EFCoreIssue;
using EFCoreIssue.Dtos;
using EFCoreIssue.Models;
using Microsoft.EntityFrameworkCore;
using Xunit;
namespace EFCoreIssue_Tests
{
public class OrdersTests
{
private readonly OrderRepository _orderRepository;
public OrdersTests()
{
_orderRepository = new OrderRepository(new OrdersDbContext());
Mapper.Initialize(cfg => cfg.CreateMap<OrderDto, Order>());
}
[Fact]
public async void Should_Update_Customer_Successfully()
{
//Arrange
var order = new Order
{
Description = "Test Order",
OrderLines = new List<OrderLine>
{
new OrderLine {ProductId = 1, Quantity = 1}
}
};
using (var dbContext = new OrdersDbContext())
{
dbContext.Orders.Add(order);
await dbContext.SaveChangesAsync();
}
//Act
var orderDto = new OrderDto
{
Description = "Test Order 2",
OrderLines = new List<OrderLineDto>
{
new OrderLineDto {OrderLineId = order.OrderLines[0].OrderLineId, ProductId = 2, Quantity = 2}
},
OrderId = order.OrderId
};
await _orderRepository.UpdateAsync(Mapper.Map<Order>(orderDto));
//Assert
using (var dbContext = new OrdersDbContext())
{
var updatedOrder = await dbContext.Orders.Include(c => c.OrderLines)
.Where(c => c.OrderId == order.OrderId).SingleAsync();
//updatedOrder.ShouldNotBeNull();
//customerFetch.Id.ShouldNotBeNull();
//customerFetch.Id.ShouldBe(customerDto.Id);
}
}
}
}
and my order repository update method
public async Task<Order> UpdateAsync(Order order)
{
var dbOrder = await GetOrderAsync(order.OrderId);
if (dbOrder == null)
throw new Exception($"Invalid Or Missing Order with Id {order.OrderId}");
_dbContext.Update(order);
await _dbContext.SaveChangesAsync();
return order;
}
Note: you can get the code on this github repository code repository
When you query the database for some record without using AsNoTracking
EF Core will start tracking it in current context. So when you update Entity in your code, EF Core finds multiple entities with same id hence the error. AsNoTracking
is one solution as you don't want EF Core to track any modifications to that. In many cases not having AsNoTracking
is fine as long as you don't add/attach/update entity with same id in the context. But it is good to have it explicitly when tracking is not required.