EF Core AddRange and entities with duplicate keys
See original GitHub issueI have a use case where a third party provides an enumeration of items that I wish to merge into a database using EF Core. There are use cases where the third party provides an item with the same key more than once in the enumeration.
Id | Account | LastPayment |
---|---|---|
12345 | ABC123 | 1/1/2021 |
23456 | BCD234 | 2/1/2021 |
12345 | ABC123 | 2/1/2021 |
Ideally, I would like BOTH updates to 12345 to take place (we audit history in the data tier).
I get an error when attempting to add 12345 twice to the same context. Code POC is:
[Fact]
public async Task HandlesDuplicateKeys()
{
var services = new ServiceCollection()
.AddDbContext<ItemContext>(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString()))
.BuildServiceProvider();
var items = new List<ItemA>()
{
new ItemA() { Id = 1, A = "Foo" },
new ItemA() { Id = 1, A = "Bar" }
};
using (var context = services.GetRequiredService<ItemContext>())
{
context.AList.AddRange(items);
await context.SaveChangesAsync();
}
}
public class ItemA
{
public int Id { get; set; }
public string? A { get; set; }
}
public class ItemContext : DbContext
{
public RepositoryContext(DbContextOptions<RepositoryContext> options) : base(options)
{ }
public DbSet<ItemA> AList { get; set; }
}
yields:
Message: System.InvalidOperationException : The instance of entity type ‘ItemA’ 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. Consider using ‘DbContextOptionsBuilder.EnableSensitiveDataLogging’ to see the conflicting key values.
What’s the appropriate way to manage this use case?
Issue Analytics
- State:
- Created 2 years ago
- Comments:10 (7 by maintainers)
I think SetValues sets all matching properties and may not meet your “non-null properties” requirement, so some reflection might be required (assuming you’re aiming to code once for multiple types and not just
ItemA
)@epatrick you don’t need to call SaveChanges on a per-item basis - note that it’s outside of the loop in the above samples. You’re indeed correct that batching multiple updates in a single SaveChanges can be important for performance.