-
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
OrderBy lifting specification #16226
Comments
Also see #16086 |
Here is a different take on the transformations implemented in EF4, using a different notation that may be more helpful. I haven't gone trough it yet to verify if there are any incongruities.
|
Bugs fixed - Allow ToList on collection projection to be called with less derived type - Project out necessary identifiers for collection join when pushing down. (Except distinct case) - Lift ordering from inner collection to generated ordered result (This would be improved further by #16226) - Map Cast from Enumerable to Queryable - Translate AsQueryable as no-op Resolves #12098 Resolves #15043 Resolves #15611 Part of #15711
Bugs fixed - Allow ToList on collection projection to be called with less derived type - Project out necessary identifiers for collection join when pushing down. (Except distinct case) - Lift ordering from inner collection to generated ordered result (This would be improved further by #16226) - Map Cast from Enumerable to Queryable - Translate AsQueryable as no-op Resolves #12098 Resolves #15043 Resolves #15611 Part of #15711
Bugs fixed - Allow ToList on collection projection to be called with less derived type - Project out necessary identifiers for collection join when pushing down. (Except distinct case) - Lift ordering from inner collection to generated ordered result (This would be improved further by #16226) - Map Cast from Enumerable to Queryable - Translate AsQueryable as no-op Resolves #12098 Resolves #15043 Resolves #15611 Part of #15711
I have added some observations #16086 (comment) While a lot of things in above specification seem to be working already. I don't think we need to do anything anymore unless we have a customer issue. |
Putting this in the backlog to revisit and see if there are any other parts we should do. |
3 cases where we are differing from original specification
|
Removing from milestone to discuss with the team. |
We decided in design meeting that we are preserving current behaviour in source code even though it does not match with specification. We will reconsider if we get a customer issue specifying current behaviour is incorrect. |
Copying here our old spec from EF 4 (produced originally by Colin Meek and I), because I think it can still help set direction for EF Core every time we need to make a decision, e.g. recently in #16144. I have made a few edits to remove details that aren't relevant, like ASP.NET WebForms query extenders or EF
DbExpression
.OrderBy Lifting for LINQ to Entities
Current behavior
The current design of LINQ to Entities requires that
OrderBy
is specified immediately before paging operators like Skip and Take/Top. Also, when certain operators (especially filters) are applied on top of a query containing anOderBy
operator, theOrderBy
is often ignored.The basic contract is that
OrderBy
applies only to the next operation in the query tree, which is in alignment with the behavior of the underlying relational database. For instance, in SQL, when composing a query on top of another query, it is illegal for the inner query to be ordered:In theory, this query could be modified to preserve the order by placing a
SELECT TOP (100) PERCENT
in the inner query, but there are no guarantees that further operations applied in the outer query will preserve the ordering (i.e. the database engine could choose a completely different index to optimize a filter operation), and therefore the end result could be that this query would be forcing an expensive sorting operation to happen in vain. For this reason, Entity Framework chooses to assume that theOrderBy
in an inner query like this can be ignored.The following query is not an actual query translated from EF, but it server the purpose of illustrating how even if the inner query specifies ordering, this one is overridden by the filter in the outer query:
Here the results of for the inner query when executed alone on a version of the Northwind sample database:
However, the results for the full query are like this:
Expected behavior
The current design differs from common user expectations and intent, in particular, of LINQ users who are accustomed to a contract in which the query processor tries to honor the semantics of
IEnumerable<T>
.In this contract,
OrderBy
becomes more of a declaration of the order in which results need to be returned thereafter, rather than as a query operator that is applied to the query results at a certain point.LINQ to Objects produces ordered results trivially because operators are applied directly to an actual in-memory sequence. LINQ to SQL on the other side will very often lift the
OrderBy
operation in the query expression to produce a behavior that is consistent with LINQ to Objects. For the aforementioned example, LINQ to SQL will most likely produce a query similar to this:This will return the expected row:
Here is another way to write this query using LINQ that clearly illustrates the significance of the gap between the current and the expected behavior:
In LINQ to SQL or LINQ to Objects, First() will correctly return the most expensive product with a name that starts with “c”.
In LINQ to Entities, on the other hand, the price is ignored and the first product that has a name that starts with “c” in an arbitrary order (generally the order of the most efficient index the database finds that optimizes the application of the filter predicate) is returned.
Proposed solution
The proposed solution consists of applying an
OrderBy
lifting operation as a step in LINQ translation . This will result in a change of the observable behavior of LINQ to Entities in the sense that the order requested will be more often preserved on query composition.Details
The basic strategy is to rewrite expressions bottom up, lifting sorts as we go. One slightly catch: as the sorted expression is lifted, it may optionally include a projection, in which case some additional rebinding may be required. The following argument patterns to collection operators are recognized:
Possible additional cases
Note on LINQSkip
LINQSkip is just the LINQ Skip operator. In EF there is a DbSkipExpression which includes an Input, a Count and a SortOrder. The LINQ Skip operator takes only an Input and a Count. DbSkip and LINQSkip therefore have different handling.
The text was updated successfully, but these errors were encountered: