EF Core设置导航属性为null时触发IsLoaded相关异常求助
问题:设置EF Core导航属性为null时触发InvalidOperationException错误
我尝试将导航属性设置为null,代码如下:
var entity = await _dbContext.Books .Include(x => x.Predecessor) .SingleOrDefaultAsync(x => x.Id == command.Book.Id, cancellationToken); if (command.Book.PredecessorId != null) { entity.Predecessor = _dbContext.Books.Find(command.Book.PredecessorId); } else { entity.Predecessor = null; } await _dbContext.SaveChangesAsync(cancellationToken);
但出现如下错误:
System.InvalidOperationException : The navigation 'Book.Predecessor' cannot have 'IsLoaded' set to false because the referenced entity is non-null and is therefore loaded. InternalEntityEntry.SetIsLoaded(INavigationBase navigation, Boolean loaded) NavigationFixer.NavigationReferenceChanged(InternalEntityEntry entry, INavigationBase navigationBase, Object oldValue, Object newValue) InternalEntityEntryNotifier.NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, Object oldValue, Object newValue) ChangeDetector.DetectNavigationChange(InternalEntityEntry entry, INavigationBase navigationBase) ChangeDetector.LocalDetectChanges(InternalEntityEntry entry) ChangeDetector.DetectChanges(IStateManager stateManager) ChangeTracker.DetectChanges() ChangeTracker.TryDetectChanges() ChangeTracker.Entries() CoreContext.OnBeforeSaving() Zeile 448
Book实体相关代码:
public partial class Book { public Book() { Successors = new List<Book>(); } public virtual ICollection<Book> Successors { get; set; } public virtual Book Predecessor { get; set; } public static void OnModelCreating(ModelBuilder modelBuilder) { OnModelCreating<Book>(modelBuilder); modelBuilder.Entity<Book>() .HasMany(p => p.Successors) .WithOne(q => q.Predecessor) .OnDelete(DeleteBehavior.ClientCascade); //... } }
问题原因
你通过Include(x => x.Predecessor)预先加载了Predecessor导航属性,此时EF Core已将该导航属性标记为已加载(IsLoaded=true)。当你把entity.Predecessor设为null时,EF Core内部会尝试将该导航属性的IsLoaded改为false,但由于之前已经加载过非null的实体,这个操作违反了EF Core的状态跟踪规则,因此抛出错误。
解决方法
有两种可行的处理方式:
方式一:直接设置外键属性而非导航属性
如果Book实体有对应的外键字段(比如PredecessorId),直接修改外键值即可,EF Core会自动同步导航属性的状态,这种方式无需预先加载Predecessor,还能避免不必要的数据查询:
var entity = await _dbContext.Books .SingleOrDefaultAsync(x => x.Id == command.Book.Id, cancellationToken); entity.PredecessorId = command.Book.PredecessorId; await _dbContext.SaveChangesAsync(cancellationToken);
方式二:若必须操作导航属性,先标记为未加载
如果一定要通过导航属性修改,可以先调用Entry(entity).Reference(x => x.Predecessor).IsLoaded = false;,再设置为null:
var entity = await _dbContext.Books .Include(x => x.Predecessor) .SingleOrDefaultAsync(x => x.Id == command.Book.Id, cancellationToken); if (command.Book.PredecessorId != null) { entity.Predecessor = _dbContext.Books.Find(command.Book.PredecessorId); } else { // 先标记导航属性为未加载 _dbContext.Entry(entity).Reference(x => x.Predecessor).IsLoaded = false; entity.Predecessor = null; } await _dbContext.SaveChangesAsync(cancellationToken);
不过这种方式不如直接操作外键简洁,更推荐方式一。
内容的提问来源于stack exchange,提问作者DarkLeafyGreen
相关产品推荐
相关产品推荐

