EFCoreでこんな感じの例外が出た。
Unhandled exception. System.InvalidOperationException: The instance of entity type 'Person' 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. at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry) ...
なんぞこれ?と思いきや、なんてことはないAsNoTracking()
付きで取得したEntityをRemoveしているだけだった。
以下検証コード
public class Person { public int Id { get; set; } } public class MyDbContext : DbContext { public DbSet<Person> Persons { get; set; } public MyDbContext(DbContextOptions options) : base(options) { Database.EnsureDeleted(); Database.EnsureCreated(); } } class Program { static async Task Main(string[] args) { var opt = new DbContextOptionsBuilder<MyDbContext>().UseSqlite("Data Source=test.db;").Options; var context = new MyDbContext(opt); var person = new Person { Id = 1 }; var person2 = new Person { Id = 2 }; context.Persons.Add(person); context.Persons.Add(person2); context.SaveChanges(); var entity1 = await context.Persons.FirstOrDefaultAsync(f => f.Id == person.Id); context.Persons.Remove(entity1); context.SaveChanges(); var entity2 = await context.Persons.AsNoTracking().FirstOrDefaultAsync(f => f.Id == person2.Id); context.Persons.Remove(entity2); // エラーが発生する部分 context.SaveChanges(); } }
余談: DbContextOptionsBuilder.EnableSensitiveDataLoggingについて
エラーメッセージにあるようにDbContextOptionsBuilder.EnableSensitiveDataLogging
で詳細なLoggingができる
using Microsoft.Extensions.Logging; // 途中省略 ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); }); var opt = new DbContextOptionsBuilder<MyDbContext>() .UseLoggerFactory(MyLoggerFactory) .EnableSensitiveDataLogging() .UseSqlite("Data Source=test.db;").Options; var context = new MyDbContext(opt);
おわり
Repositoryクラスに実装したDelete
関数がAsNoTracking
を付与したGetById
関数を使用していたためハマりました。。
注意せねば...。
以上です。