-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query: INNER JOIN generated for navigation traversals from principal to dependents #6177
Comments
I'm think the problem lies when specifying the navigation inside the |
@Bartmax can you share your entity classes and context code |
I'll try:
Let me know if you need something else, I removed a lot of stuff to make it readable, hope I included all details that matters. |
I'm not sure how |
@gdoron I don't think select |
The |
@rowanmiller great information to know!! thanks. not sure how I missed that but whatever. |
@Bartmax problems seems to be in a way that the model is setup. public class GameDetails
{
public int GameDetailsId { get; set; }
public int GameId { get; set; }
public GameFile Logo { get; set; }
...
} since GameId is a non-nullable int, EF assumes that the navigation between Game and GameDetails is a required one. Therefore INNER JOIN is produced for that navigation, which results in 2 results being "eaten". Here is the SQL generated for the first query: SELECT [g.Details].[GameDetailsId], [g.Details].[GameId], [g.Details].[LogoGameFileId], [g.Details.Logo].[GameFileId], [g.Details.Logo].[ContentType], [g.Details.Logo].[Filename], [g.Details.Logo].[OriginalFilename], [g].[Title], [g].[GameId]
FROM [Games] AS [g]
INNER JOIN [GameDetails] AS [g.Details] ON [g].[GameId] = [g.Details].[GameId]
LEFT JOIN [GameFiles] AS [g.Details.Logo] ON [g.Details].[LogoGameFileId] = [g.Details.Logo].[GameFileId]
ORDER BY [g.Details].[LogoGameFileId] This can be fixed by setting GameId to be int? - you will see correct 4 results returned. Note however that due to #4588 everything coming after first optional navigation is done on the client so in fact we will send 2 queries to the database:
and
and patch the results on the client. Problems with queries 3 and 4 are legitimate bugs and I filed separate issue for them: #6207 |
After giving it some thought, I think that the original problem is a legitimate bug also - we should not be assuming that navigation is optional when we query from principal to dependent. It is perfectly fine to have a game (principal) without the game detail (dependent), even when the FK on the game detail is not nullable |
Moving the milestone to 1.0.1 as we currently return incorrect results |
Thanks for the detailed explanation. I'm glad it also helped to discover some bugs. |
@divega ping regarding triage |
I think throwing is better than no-op, it's clear that the author meant something, that EF can not do, ignoring his mistake by swallowing the error can lead to unexpected results. |
@gdoron oh no please don't throw. a runtime warning or just no op. |
We can't throw at this point - it would be a breaking change for people who produced those queries based on 1.0.0 |
@maumar can you help us confirm the scope of the bug (and of the potential fix), e.g. does it only affect 1:0..1 relationships? Does it affect |
+1 for a runtime warning as it is a confusing behaviour. |
@divega it only affects 1:0..1 relationships - collections are fine, includes are fine. |
…from principal to dependents Problem was that during navigation expansion we assumed that when Foreign Key is required, then automatically INNER JOIN could be produced to associate entities within the navigation. This is only true if the navigation is from dependent to principal. If navigation is from principal to dependent need to produce LEFT OUTER JOIN as it is valid to have a principal entity without dependent in that case.
…from principal to dependents Problem was that during navigation expansion we assumed that when Foreign Key is required, then automatically INNER JOIN could be produced to associate entities within the navigation. This is only true if the navigation is from dependent to principal. If navigation is from principal to dependent need to produce LEFT OUTER JOIN as it is valid to have a principal entity without dependent in that case.
@maumar could we fix the issues with null compensation when functions are evaluated in memory for 1.0.1? If we can't then we are not completely comfortable with taking this in 1.0.1. |
…from principal to dependents Problem was that during navigation expansion we assumed that when Foreign Key is required, then automatically INNER JOIN could be produced to associate entities within the navigation. This is only true if the navigation is from dependent to principal. If navigation is from principal to dependent need to produce LEFT OUTER JOIN as it is valid to have a principal entity without dependent in that case.
…from principal to dependents Problem was that during navigation expansion we assumed that when Foreign Key is required, then automatically INNER JOIN could be produced to associate entities within the navigation. This is only true if the navigation is from dependent to principal. If navigation is from principal to dependent need to produce LEFT OUTER JOIN as it is valid to have a principal entity without dependent in that case.
Fixed in 2888a86 |
This is technically a different issue, but it is related to this thread. It seems that you are not able pull certain properties out of the dependent objects in the query. Instead you need to return the entire object from the database and then inspect the properties you are concerned about. This code fails : this code works fine, but we are forced to return the entire object. |
This is very counter intuitive, I'm not sure what's going on.
My goal was to get the 4 database entries, with null on Logo when Details is null.
I tried different approaches and i found it very inconsistent.
So obvious question is: is it possible to get all rows, left join with default if empty?
Question 2: Am I doing something wrong or this is a bug?
Database has 4 entries, 2 with null details.
this query returns 2 results:
this query returns 4 results:
this query throws:
InvalidOperationException: Operation is not valid due to the current state of the object. GetArgument
this query throws:
SqlException: The column prefix 't10' does not match with a table name or alias name used in the query. No column name was specified for column 1 of 't2'. Invalid column name 'Title'. Invalid column name 'GameId'.
this query returns 4 elements, same as query2. (logo is explicit set as null):
note: using
g.Details?.Logo
on lambda does not compile.The text was updated successfully, but these errors were encountered: