When doing an AsNotTracking() query with an eager fetch, EF incorrectly throws a DetachedLazyLoadingWarning when accessing the navigation which was eagerly loaded. This appears to happen when the principle entity is in a hierarchy.
var user = await db.Users
.AsQueryable()
.AsNoTracking()
.Include(x => x.PullRequests)
.FirstOrDefaultAsync();
var prs = user.PullRequests; // Throws here
*Repro to follow in comment.
EF Core version: 3.1.1
Database provider: Npgsql & InMemory
Target framework: .NET Core 3.1
Run solution and find exception printed in the Console.
@ajcvickers
Is there no way this gets fixed in a bug fix for 3.1.x?
3.1 is LTS, this is clearly a bug, and it affects every simple no-tracking query w/ Include and a hierarchy.
@Cooksauce It's unlikely since the workaround (i.e. disable the warning) is very easy. If we see multiple customers hitting this, then we will reconsider.
@Cooksauce FYI, the release planning process goes into more details on the patch bar.
@ajcvickers
Can this be marked with the 'regression' label? The query works in 2.2
Here's my case for this under those parameters:
Respectfully, I don't agree with disabling the warning (_and will not use that in production_) unless you can point me to what the specific behavior is when the warning should throw, but was disabled. What if, in a different area of code, I actually do try to lazy load on a non-tracked entity? What happens? Does it just return an empty collection? Does it return null?
If it returns an empty collection, how do I know whether the collection is actually empty or I was trying to lazy load on an untracked entity?
Ultimately, I'm just trying to readonly stream a very large collection of entities, and not have the change-tracker slow anything down. Do you have any other workarounds to suggest?
@Cooksauce When the warning was implemented there was debate as to whether it was really useful. EF6 didn't have any such warning. If you disable the warning, then EF will no-op. It won't load the relationship; it won't create any collection that doesn't already exist.
The most likely outcome is that you'll get a null ref exception at the next line in your code instead of this warning. The intention of the warning is to help you track down the issue, but it's not functionally very important.