efcore: Multiple Include() warning: false positive for ThenInclude()?
There is warning that protects us against inadvertent cross products. It occurs when we use multiple calls to Include()
in a single query:
Compiling a query which loads related collections for more than one collection navigation,
either via 'Include' or through projection, but no 'QuerySplittingBehavior' has been configured.
By default, Entity Framework will use 'QuerySplittingBehavior.SingleQuery',
which can potentially result in slow query performance.
See https://go.microsoft.com/fwlink/?linkid=2134277 for more information.
To identify the query that's triggering this warning call
'ConfigureWarnings(w => w.Throw(RelationalEventId.MultipleCollectionIncludeWarning))'.
We can suppress it with an explicit call to .AsSingleQuery()
if we know what we are doing.
To my surprise, the warning also pops up for .Include(parent => parent.Child).ThenInclude(child => child.Grandchild)
.
The reason I am surprised is that this seems to give the warning whenever ThenInclude()
is used, as if that entire method should be avoided. Moreover, an Include()
followed by a ThenInclude()
does not introduce a cross product and thus seems no cause for concern.
Is this a false positive?
About this issue
- Original URL
- State: open
- Created 2 years ago
- Comments: 17 (12 by maintainers)
Design decision: we’ll change the warning to cover cross products only (multiple sibling collection joins, Cartesian explosion).
We’ll leave split query the way it is: we’re splitting the query because the user explicitly told us to (opt-in), and data duplication can still be significant in some scenarios. Also, a change here would obviously be more extreme than the warning change, and there’s no huge downside in splitting the query given that the user has already opted into it (especially after #10878).
Changing the actual behavior of split query is a bit more extreme, i.e. stop doing split query for the Include/ThenInclude case (but I agree it may make sense (and would be consistent with the warning discussion). We’ll discuss.
We should discuss this, but I was always under the impression that this warning was intended to warn against the cartesian explosion specifically.
@Timovzl @stevendarby you are of course right about the order of magnitude difference between cross product (Cartesian explosion) resulting from mulitple sibling collection joins, and the far less problematic duplication resulting from joins in general.
The original intent behind the warning was to cover both; note that the warning doesn’t say Cartesian explosion or cross product; just that multiple collection navigations are being included. But I agree that the two are sufficiently different so as at least warrant different warnings (or possibly not warn for duplication in general).
I also agree that our documentation conflates the two concepts a bit. I’ll make that better.
Note: I’ve submitted https://github.com/dotnet/EntityFramework.Docs/pull/4201 to improve our docs around this.
Thanks @roji. I know at this point no decision has been made and it needs further thought but: suppose you do go down the path of not warning for duplication in general, for me it would also beg the question of whether it’s correct for split query to “kick in” in the presence of any collection navigation rather than just those that produce a cross product. Note that there is already a slight disconnect in that it splits on the first collection but wouldn’t warn about this without a second collection included. Anyway, perhaps just something to consider alongside this in future 😃
@roji I’m assuming one-to-many because I took it as a given that the warning does not appear until 2 collections are involved and interpreted the surprise at the warning being due to the fact the query doesn’t produce a cross product (which is true) and not because it didn’t involve collections. However, I’m making assumptions and agree the naming of the navigations suggests one-to-one, so will zip it till OP provides a sample as you requested 😃
@Timovzl can you submit a minimal code sample for this?