如何避免EF Core 3.1在Where子句中自动加载关联实体
问题原因
这不是延迟加载导致的,而是EF Core的跟踪查询自动填充导航属性特性:
- 通过
accountService.GetByEmailOrUsername(userName)获取的account实体,正被当前DbContext实例跟踪 - 执行Order查询时,EF Core会检查已跟踪的实体,发现Order的
UserId和已跟踪的User的Id完全匹配,就会自动将Order的User导航属性赋值为已存在的跟踪实体 - 这个行为和导航属性是否标记为
virtual、延迟加载是否开启无关,是EF Core跟踪查询的默认行为
解决方法
可以通过以下几种方式禁用这个自动填充行为:
1. 使用无跟踪查询
在查询Order时添加AsNoTracking(),让DbContext不跟踪查询结果,就不会自动关联已跟踪的User实体:
// 修改GetByField方法,增加无跟踪选项 public Task<IEnumerable<T>> GetByField<T>(Expression<Func<T, bool>> where, bool noTracking = false, params string[] routes) where T : class, new() { IQueryable<T> inc = context.Set<T>(); if (noTracking) { inc = inc.AsNoTracking(); } if (routes.Length > 0) { foreach (string route in routes) { inc = inc.Include(route); } } var results = inc.Where(where).AsEnumerable(); return Task.FromResult(results); } // 调用时传入noTracking=true var orderHistory = (await dataService.GetByField<Order>(model => model.UserId == account.Id && model.TotalHours != 0, true)).ToList();
2. 解除对User实体的跟踪
获取account后,手动将其从DbContext的跟踪状态中移除,后续查询Order时就不会自动填充导航属性:
var account = await accountService.GetByEmailOrUsername(userName); // 解除实体跟踪 context.Entry(account).State = EntityState.Detached; var orderHistory = (await dataService.GetByField<Order>(model => model.UserId == account.Id && model.TotalHours != 0)).ToList();
3. 使用独立的DbContext实例
如果获取account和查询Order使用不同的DbContext实例,两个实例的跟踪状态相互隔离,不会出现自动填充导航属性的情况。这种方式需要调整架构,比如每个请求实例化一次DbContext。
内容的提问来源于stack exchange,提问作者Erick Velasco
相关产品推荐
相关产品推荐

