1
//---------------------------------------------------------------------
2 // <copyright file="OrderByLifter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 //---------------------------------------------------------------------
9 namespace System
.Data
.Objects
.ELinq
11 using System
.Data
.Common
.CommandTrees
;
12 using System
.Data
.Common
.CommandTrees
.ExpressionBuilder
;
13 using System
.Data
.Common
.Utils
;
14 using System
.Data
.Metadata
.Edm
;
15 using System
.Diagnostics
;
17 internal sealed partial class ExpressionConverter
20 /// A context-sensitive DbExpression builder class that simulates order preservation
21 /// for operators (project, filter, oftype, skip and limit) that are not natively order
22 /// preserving. The builder simulates order preservation by 'lifting' order keys in
23 /// the expression tree. For instance, source.Sort(o).Where(f) is rewritten as
24 /// source.Where(f).Sort(o) since otherwise the sort keys would be ignored.
26 /// In general, the lifter works as follows:
28 /// - The input to the operator is matched against a series of patterns for intrinsically
29 /// ordered expressions.
30 /// - For each pattern, the lifter encodes the compensation required for each of the
31 /// lifting operators that can be applied.
33 private sealed class OrderByLifter
35 private readonly AliasGenerator _aliasGenerator
;
37 internal OrderByLifter(AliasGenerator aliasGenerator
)
39 _aliasGenerator
= aliasGenerator
;
42 #region 'Public' builder methods.
43 internal DbExpression
Project(DbExpressionBinding input
, DbExpression projection
)
45 OrderByLifterBase lifter
= GetLifter(input
.Expression
);
46 return lifter
.Project(input
.Project(projection
));
49 internal DbExpression
Filter(DbExpressionBinding input
, DbExpression predicate
)
51 OrderByLifterBase lifter
= GetLifter(input
.Expression
);
52 return lifter
.Filter(input
.Filter(predicate
));
55 internal DbExpression
OfType(DbExpression argument
, TypeUsage type
)
57 OrderByLifterBase lifter
= GetLifter(argument
);
58 return lifter
.OfType(type
);
61 internal DbExpression
Skip(DbExpressionBinding input
, DbExpression skipCount
)
63 OrderByLifterBase lifter
= GetLifter(input
.Expression
);
64 return lifter
.Skip(skipCount
);
67 internal DbExpression
Limit(DbExpression argument
, DbExpression limit
)
69 OrderByLifterBase lifter
= GetLifter(argument
);
70 return lifter
.Limit(limit
);
74 private OrderByLifterBase
GetLifter(DbExpression root
)
76 return OrderByLifterBase
.GetLifter(root
, _aliasGenerator
);
79 private abstract class OrderByLifterBase
81 protected readonly DbExpression _root
;
82 protected readonly AliasGenerator _aliasGenerator
;
84 protected OrderByLifterBase(DbExpression root
, AliasGenerator aliasGenerator
)
87 _aliasGenerator
= aliasGenerator
;
91 /// Returns a lifter instance which supports lifting the intrinsic order of the given
92 /// source expression across specific operations (filter, project, oftype, skip, and limit)
95 /// Lifting only occurs for expressions that are ordered. Each of the nested
96 /// OrderByLifterBase class implementations represents one or two of the ordered patterns with
97 /// the exception of the PassthroughOrderByLifter. The latter class represents expressions
98 /// without intrinsic order that therefore require no lifting.
100 internal static OrderByLifterBase
GetLifter(DbExpression source
, AliasGenerator aliasGenerator
)
102 if (source
.ExpressionKind
== DbExpressionKind
.Sort
)
104 return new SortLifter((DbSortExpression
)source
, aliasGenerator
);
106 if (source
.ExpressionKind
== DbExpressionKind
.Project
)
108 var project
= (DbProjectExpression
)source
;
109 DbExpression projectInput
= project
.Input
.Expression
;
110 if (projectInput
.ExpressionKind
== DbExpressionKind
.Sort
)
112 return new ProjectSortLifter(project
, (DbSortExpression
)projectInput
, aliasGenerator
);
114 if (projectInput
.ExpressionKind
== DbExpressionKind
.Skip
)
116 return new ProjectSkipLifter(project
, (DbSkipExpression
)projectInput
, aliasGenerator
);
118 if (projectInput
.ExpressionKind
== DbExpressionKind
.Limit
)
120 var limit
= (DbLimitExpression
)projectInput
;
121 DbExpression limitInput
= limit
.Argument
;
122 if (limitInput
.ExpressionKind
== DbExpressionKind
.Sort
)
124 return new ProjectLimitSortLifter(project
, limit
, (DbSortExpression
)limitInput
, aliasGenerator
);
126 if (limitInput
.ExpressionKind
== DbExpressionKind
.Skip
)
128 return new ProjectLimitSkipLifter(project
, limit
, (DbSkipExpression
)limitInput
, aliasGenerator
);
132 if (source
.ExpressionKind
== DbExpressionKind
.Skip
)
134 return new SkipLifter((DbSkipExpression
)source
, aliasGenerator
);
136 if (source
.ExpressionKind
== DbExpressionKind
.Limit
)
138 var limit
= (DbLimitExpression
)source
;
139 DbExpression limitInput
= limit
.Argument
;
140 if (limitInput
.ExpressionKind
== DbExpressionKind
.Sort
)
142 return new LimitSortLifter(limit
, (DbSortExpression
)limitInput
, aliasGenerator
);
144 if (limitInput
.ExpressionKind
== DbExpressionKind
.Skip
)
146 return new LimitSkipLifter(limit
, (DbSkipExpression
)limitInput
, aliasGenerator
);
148 if (limitInput
.ExpressionKind
== DbExpressionKind
.Project
)
150 var project
= (DbProjectExpression
)limitInput
;
151 DbExpression projectInput
= project
.Input
.Expression
;
152 if (projectInput
.ExpressionKind
== DbExpressionKind
.Sort
)
154 // source.Sort(o).Project(p).Limit(k).* is equivalent to transformation for
155 // source.Sort(o).Limit(k).Project(p).*
156 return new ProjectLimitSortLifter(project
, limit
, (DbSortExpression
)projectInput
, aliasGenerator
);
158 if (projectInput
.ExpressionKind
== DbExpressionKind
.Skip
)
160 // source.Skip(k, o).Project(p).Limit(k2).* is equivalent to transformation for
161 // source.Skip(k, o).Limit(k2).Project(p).*
162 return new ProjectLimitSkipLifter(project
, limit
, (DbSkipExpression
)projectInput
, aliasGenerator
);
166 return new PassthroughOrderByLifter(source
, aliasGenerator
);
169 #region Builder methods
170 internal abstract DbExpression
Project(DbProjectExpression project
);
171 internal abstract DbExpression
Filter(DbFilterExpression filter
);
172 internal virtual DbExpression
OfType(TypeUsage type
)
174 // s.OfType<T> is normally translated to s.Filter(e => e is T).Project(e => e as T)
175 DbExpressionBinding rootBinding
= _root
.BindAs(_aliasGenerator
.Next());
176 DbExpression filter
= this.Filter(rootBinding
.Filter(rootBinding
.Variable
.IsOf(type
)));
177 OrderByLifterBase filterLifter
= GetLifter(filter
, _aliasGenerator
);
178 DbExpressionBinding filterBinding
= filter
.BindAs(_aliasGenerator
.Next());
179 DbExpression project
= filterLifter
.Project(filterBinding
.Project(filterBinding
.Variable
.TreatAs(type
)));
182 internal abstract DbExpression
Limit(DbExpression k
);
183 internal abstract DbExpression
Skip(DbExpression k
);
186 #region Lambda composition: merge arguments to operators to create a single operator
187 protected DbProjectExpression
ComposeProject(DbExpression input
, DbProjectExpression first
, DbProjectExpression second
)
189 // source.Project(first).Project(second) -> source.Project(e => second(first(e)))
191 // create lambda expression representing the second projection (e => second(e))
192 DbLambda secondLambda
= DbExpressionBuilder
.Lambda(second
.Projection
, second
.Input
.Variable
);
194 // invoke lambda with variable from the first projection
195 DbProjectExpression composed
= first
.Input
.Project(secondLambda
.Invoke(first
.Projection
));
197 return RebindProject(input
, composed
);
200 protected DbFilterExpression
ComposeFilter(DbExpression input
, DbProjectExpression first
, DbFilterExpression second
)
202 // source.Project(first).Filter(second) -> source.Filter(e => second(first(e)))
204 // create lambda expression representing the filter (e => second(e))
205 DbLambda secondLambda
= DbExpressionBuilder
.Lambda(second
.Predicate
, second
.Input
.Variable
);
207 // invoke lambda with variable from the project
208 DbFilterExpression composed
= first
.Input
.Filter(secondLambda
.Invoke(first
.Projection
));
210 return RebindFilter(input
, composed
);
214 #region Paging op reducers
215 protected DbSkipExpression
AddToSkip(DbExpression input
, DbSkipExpression skip
, DbExpression plusK
)
217 // source.Skip(k, o).Skip(k2) -> source.Skip(k + k2, o)
218 DbExpression newCount
= CombineIntegers(skip
.Count
, plusK
,
220 return RebindSkip(input
, skip
, newCount
);
223 protected DbLimitExpression
SubtractFromLimit(DbExpression input
, DbLimitExpression limit
, DbExpression minusK
)
225 DbExpression newCount
= CombineIntegers(limit
.Limit
, minusK
,
226 (l
, r
) => r
> l
? 0 : l
- r
); // can't limit to less than zero rows)
227 return DbExpressionBuilder
.Limit(input
, newCount
);
230 protected DbLimitExpression
MinimumLimit(DbExpression input
, DbLimitExpression limit
, DbExpression k
)
232 // source.Limit(k).Limit(k2) -> source.Limit(Min(k, k2))
233 DbExpression newCount
= CombineIntegers(limit
.Limit
, k
, Math
.Min
);
234 return DbExpressionBuilder
.Limit(input
, newCount
);
237 protected DbExpression
CombineIntegers(DbExpression left
, DbExpression right
,
238 Func
<int, int, int> combineConstants
)
240 if (left
.ExpressionKind
== DbExpressionKind
.Constant
&&
241 right
.ExpressionKind
== DbExpressionKind
.Constant
)
243 object leftValue
= ((DbConstantExpression
)left
).Value
;
244 object rightValue
= ((DbConstantExpression
)right
).Value
;
245 if (leftValue
is int && rightValue
is int)
247 return left
.ResultType
.Constant(combineConstants((int)leftValue
, (int)rightValue
));
250 Debug
.Fail("only valid for integer constants");
251 throw EntityUtil
.InternalError(EntityUtil
.InternalErrorCode
.UnexpectedLinqLambdaExpressionFormat
);
255 #region Rebinders: take an operator and apply it to a different input
256 protected DbProjectExpression
RebindProject(DbExpression input
, DbProjectExpression project
)
258 DbExpressionBinding inputBinding
= input
.BindAs(project
.Input
.VariableName
);
259 return inputBinding
.Project(project
.Projection
);
262 protected DbFilterExpression
RebindFilter(DbExpression input
, DbFilterExpression filter
)
264 DbExpressionBinding inputBinding
= input
.BindAs(filter
.Input
.VariableName
);
265 return inputBinding
.Filter(filter
.Predicate
);
268 protected DbSortExpression
RebindSort(DbExpression input
, DbSortExpression sort
)
270 DbExpressionBinding inputBinding
= input
.BindAs(sort
.Input
.VariableName
);
271 return inputBinding
.Sort(sort
.SortOrder
);
274 protected DbSortExpression
ApplySkipOrderToSort(DbExpression input
, DbSkipExpression sortSpec
)
276 DbExpressionBinding inputBinding
= input
.BindAs(sortSpec
.Input
.VariableName
);
277 return inputBinding
.Sort(sortSpec
.SortOrder
);
280 protected DbSkipExpression
ApplySortOrderToSkip(DbExpression input
, DbSortExpression sort
, DbExpression k
)
282 DbExpressionBinding inputBinding
= input
.BindAs(sort
.Input
.VariableName
);
283 return inputBinding
.Skip(sort
.SortOrder
, k
);
286 protected DbSkipExpression
RebindSkip(DbExpression input
, DbSkipExpression skip
, DbExpression k
)
288 DbExpressionBinding inputBinding
= input
.BindAs(skip
.Input
.VariableName
);
289 return inputBinding
.Skip(skip
.SortOrder
, k
);
295 /// Represents an expression of the form: source.Skip(k, o).Limit(k2)
297 private class LimitSkipLifter
: OrderByLifterBase
299 private readonly DbLimitExpression _limit
;
300 private readonly DbSkipExpression _skip
;
301 private readonly DbExpression _source
;
303 internal LimitSkipLifter(DbLimitExpression limit
, DbSkipExpression skip
, AliasGenerator aliasGenerator
)
304 : base(limit
, aliasGenerator
)
308 _source
= skip
.Input
.Expression
;
311 internal override DbExpression
Filter(DbFilterExpression filter
)
313 // source.Skip(k, o).Limit(k2).Filter(f) ->
314 // source.Skip(k, o).Limit(k2).Filter(f).Sort(o)
315 return ApplySkipOrderToSort(filter
, _skip
);
318 internal override DbExpression
Project(DbProjectExpression project
)
320 // the result is already ordered (no compensation is required)
324 internal override DbExpression
Limit(DbExpression k
)
326 // source.Skip(k, o).Limit(k2).Limit(k3) ->
327 // source.Skip(k, o).Limit(Min(k2, k3)) where k2 and k3 are constants
328 // otherwise source.Skip(k, o).Limit(k2).Sort(o).Limit(k3)
329 if (_limit
.Limit
.ExpressionKind
== DbExpressionKind
.Constant
&&
330 k
.ExpressionKind
== DbExpressionKind
.Constant
)
332 return MinimumLimit(_skip
, _limit
, k
);
336 return ApplySkipOrderToSort(_limit
, _skip
).Limit(k
);
340 internal override DbExpression
Skip(DbExpression k
)
342 // source.Skip(k, o).Limit(k2).Skip(k3) ->
343 // source.Skip(k, o).Limit(k2).Skip(k3, o)
344 return RebindSkip(_limit
, _skip
, k
);
349 /// Represents an expression of the form: source.Sort(o).Limit(k)
351 private class LimitSortLifter
: OrderByLifterBase
353 private readonly DbLimitExpression _limit
;
354 private readonly DbSortExpression _sort
;
355 private readonly DbExpression _source
;
357 internal LimitSortLifter(DbLimitExpression limit
, DbSortExpression sort
, AliasGenerator aliasGenerator
)
358 : base(limit
, aliasGenerator
)
362 _source
= sort
.Input
.Expression
;
365 internal override DbExpression
Filter(DbFilterExpression filter
)
367 // source.Sort(o).Limit(k).Filter(f) -> source.Sort(o).Limit(k).Filter(f).Sort(o)
368 return RebindSort(filter
, _sort
);
371 internal override DbExpression
Project(DbProjectExpression project
)
373 // the result is already ordered (no compensation is required)
377 internal override DbExpression
Limit(DbExpression k
)
379 // source.Sort(o).Limit(k).Limit(k2) -> source.Sort(o).Limit(Min(k, k2)) when k and k2 are constants
380 // otherwise -> source.Sort(o).Limit(k).Sort(o).Limit(k2)
381 if (_limit
.Limit
.ExpressionKind
== DbExpressionKind
.Constant
&&
382 k
.ExpressionKind
== DbExpressionKind
.Constant
)
384 return MinimumLimit(_sort
, _limit
, k
);
388 return RebindSort(_limit
, _sort
).Limit(k
);
392 internal override DbExpression
Skip(DbExpression k
)
394 // source.Sort(o).Limit(k).Skip(k2) -> source.Sort(o).Limit(k).Skip(k2, o)
395 return ApplySortOrderToSkip(_limit
, _sort
, k
);
400 /// Represents an expression of the form: source.Skip(k, o).Limit(k2).Project(p)
403 /// This class is also used to represent expressions of the form: source.Skip(k, o).Project(p).Limit(k).
404 /// As a result, the rewrites must be spelled out entirely (the implementation cannot assume that
405 /// _limit exists in a particular position in the tree)
407 private class ProjectLimitSkipLifter
: OrderByLifterBase
409 private readonly DbProjectExpression _project
;
410 private readonly DbLimitExpression _limit
;
411 private readonly DbSkipExpression _skip
;
412 private readonly DbExpression _source
;
414 internal ProjectLimitSkipLifter(DbProjectExpression project
, DbLimitExpression limit
, DbSkipExpression skip
, AliasGenerator aliasGenerator
)
415 : base(project
, aliasGenerator
)
420 _source
= skip
.Input
.Expression
;
423 internal override DbExpression
Filter(DbFilterExpression filter
)
425 // source.Skip(k, o).Limit(k2).Project(p).Filter(f) ->
426 // source.Skip(k, o).Limit(k2).Filter(e => f(p(e))).Sort(o).Project(p)
427 return RebindProject(
428 ApplySkipOrderToSort(
430 DbExpressionBuilder
.Limit(_skip
, _limit
.Limit
),
437 internal override DbExpression
Project(DbProjectExpression project
)
439 // source.Skip(k, o).Limit(k2).Project(p).Project(p2) ->
440 // source.Skip(k, o).Limit(k2).Project(e => p2(p(e)))
441 return ComposeProject(
442 DbExpressionBuilder
.Limit(_skip
, _limit
.Limit
),
447 internal override DbExpression
Limit(DbExpression k
)
449 // source.Skip(k, o).Limit(k2).Project(p).Limit(k3) ->
450 // source.Skip(k, o).Limit(Min(k2, k3)).Project(p) where k2 and k2 are constants
451 // otherwise -> source.Skip(k, o).Limit(k2).Sort(o).Limit(k3).Project(p)
452 if (_limit
.Limit
.ExpressionKind
== DbExpressionKind
.Constant
&&
453 k
.ExpressionKind
== DbExpressionKind
.Constant
)
455 return RebindProject(
456 MinimumLimit(_skip
, _limit
, k
),
461 return RebindProject(
462 DbExpressionBuilder
.Limit(
463 ApplySkipOrderToSort(
464 DbExpressionBuilder
.Limit(_skip
, _limit
.Limit
),
471 internal override DbExpression
Skip(DbExpression k
)
473 // source.Skip(k, o).Limit(k2).Project(p).Skip(k3) ->
474 // source.Skip(k + k3, o).Limit(k2 – k3).Project(p) when k, k2 and k3 are constants
475 // otherwise -> source.Skip(k, o).Limit(k2).Skip(k3, o).Project(p)
476 if (_skip
.Count
.ExpressionKind
== DbExpressionKind
.Constant
&&
477 _limit
.Limit
.ExpressionKind
== DbExpressionKind
.Constant
&&
478 k
.ExpressionKind
== DbExpressionKind
.Constant
)
480 return RebindProject(
482 AddToSkip(_source
, _skip
, k
),
489 return RebindProject(
491 DbExpressionBuilder
.Limit(_skip
, _limit
.Limit
),
500 /// Represents an expression of the form: source.Sort(o).Limit(k).Project(p)
503 /// This class is also used to represent expressions of the form: source.Sort(o).Project(p).Limit(k).
504 /// As a result, the rewrites must be spelled out entirely (the implementation cannot assume that
505 /// _limit exists in a particular position in the tree)
507 private class ProjectLimitSortLifter
: OrderByLifterBase
509 private readonly DbProjectExpression _project
;
510 private readonly DbLimitExpression _limit
;
511 private readonly DbSortExpression _sort
;
512 private readonly DbExpression _source
;
514 internal ProjectLimitSortLifter(DbProjectExpression project
, DbLimitExpression limit
, DbSortExpression sort
, AliasGenerator aliasGenerator
)
515 : base(project
, aliasGenerator
)
520 _source
= sort
.Input
.Expression
;
523 internal override DbExpression
Filter(DbFilterExpression filter
)
525 // source.Sort(o).Limit(k).Project(p).Filter(f) -> source.Sort(o).Limit(k).Filter(e => f(p(e))).Sort(o).Project(p)
526 return RebindProject(
529 DbExpressionBuilder
.Limit(_sort
, _limit
.Limit
),
536 internal override DbExpression
Project(DbProjectExpression project
)
538 // source.Sort(o).Limit(k).Project(p).Project(p2) -> source.Sort(o).Limit(k).Project(e => p2(p(e)))
539 return ComposeProject(
540 DbExpressionBuilder
.Limit(_sort
, _limit
.Limit
),
545 internal override DbExpression
Limit(DbExpression k
)
547 // source.Sort(o).Limit(k).Project(p).Limit(k2) -> source.Sort(o).Limit(Min(k, k2)).Project(p) where k and k2 are constants
548 // otherwise -> source.Sort(o).Limit(k).Sort(o).Limit(k2).Project(p)
549 if (_limit
.Limit
.ExpressionKind
== DbExpressionKind
.Constant
&&
550 k
.ExpressionKind
== DbExpressionKind
.Constant
)
552 return RebindProject(
553 MinimumLimit(_sort
, _limit
, k
),
558 return RebindProject(
559 DbExpressionBuilder
.Limit(
561 DbExpressionBuilder
.Limit(_sort
, _limit
.Limit
),
568 internal override DbExpression
Skip(DbExpression k
)
570 // source.Sort(o).Limit(k).Project(p).Skip(k2) -> source.Sort(o).Limit(k).Skip(k2, o).Project(p)
571 return RebindProject(
572 ApplySortOrderToSkip(
573 DbExpressionBuilder
.Limit(_sort
, _limit
.Limit
),
581 /// Represents an expression of the form: source.Skip(k, o).Project(p)
583 private class ProjectSkipLifter
: OrderByLifterBase
585 private readonly DbProjectExpression _project
;
586 private readonly DbSkipExpression _skip
;
587 private readonly DbExpression _source
;
589 internal ProjectSkipLifter(DbProjectExpression project
, DbSkipExpression skip
, AliasGenerator aliasGenerator
)
590 : base(project
, aliasGenerator
)
594 _source
= _skip
.Input
.Expression
;
597 internal override DbExpression
Filter(DbFilterExpression filter
)
599 // source.Skip(k, o).Project(p).Filter(f) -> source.Skip(k, o).Filter(e => f(p(e))).Sort(o).Project(p)
600 return RebindProject(
601 ApplySkipOrderToSort(
602 ComposeFilter(_skip
, _project
, filter
),
607 internal override DbExpression
Limit(DbExpression k
)
609 // the result is already ordered (no compensation is required)
610 return DbExpressionBuilder
.Limit(_root
, k
);
613 internal override DbExpression
Project(DbProjectExpression project
)
615 // source.Skip(k, o).Project(p).Project(p2) -> source.Skip(k, o).Project(e => p2(p(e)))
616 return ComposeProject(_skip
, _project
, project
);
619 internal override DbExpression
Skip(DbExpression k
)
621 // source.Skip(k, o).Project(p).Skip(k2) -> source.Skip(k + k2, o).Project(p) where k and k2 are constants,
622 // otherwise -> source.Skip(k, o).Skip(k2, o).Project(p)
623 if (_skip
.Count
.ExpressionKind
== DbExpressionKind
.Constant
&&
624 k
.ExpressionKind
== DbExpressionKind
.Constant
)
626 return RebindProject(AddToSkip(_source
, _skip
, k
), _project
);
630 return RebindProject(RebindSkip(_skip
, _skip
, k
), _project
);
636 /// Represents an expression of the form: source.Skip(k, o)
638 private class SkipLifter
: OrderByLifterBase
640 private readonly DbSkipExpression _skip
;
641 private readonly DbExpression _source
;
643 internal SkipLifter(DbSkipExpression skip
, AliasGenerator aliasGenerator
)
644 : base(skip
, aliasGenerator
)
647 _source
= skip
.Input
.Expression
;
650 internal override DbExpression
Filter(DbFilterExpression filter
)
652 // source.Skip(k, o).Filter(f) -> source.Skip(k, o).Filter(f).Sort(o)
653 return ApplySkipOrderToSort(filter
, _skip
);
656 internal override DbExpression
Project(DbProjectExpression project
)
658 // the result is already ordered (no compensation is required)
662 internal override DbExpression
Limit(DbExpression k
)
664 // the result is already ordered (no compensation is required)
665 return DbExpressionBuilder
.Limit(_root
, k
);
668 internal override DbExpression
Skip(DbExpression k
)
670 // source.Skip(k, o).Skip(k2) -> source.Skip(k + k2, o) where k and k2 are both constants
671 // otherwise, -> source.Skip(k, o).Skip(k2, o)
672 if (_skip
.Count
.ExpressionKind
== DbExpressionKind
.Constant
&&
673 k
.ExpressionKind
== DbExpressionKind
.Constant
)
675 return AddToSkip(_source
, _skip
, k
);
679 return RebindSkip(_skip
, _skip
, k
);
685 /// Represents an expression of the form: source.Sort(o).Project(p)
687 private class ProjectSortLifter
: OrderByLifterBase
689 private readonly DbProjectExpression _project
;
690 private readonly DbSortExpression _sort
;
691 private readonly DbExpression _source
;
693 internal ProjectSortLifter(DbProjectExpression project
, DbSortExpression sort
, AliasGenerator aliasGenerator
)
694 : base(project
, aliasGenerator
)
698 _source
= sort
.Input
.Expression
;
701 internal override DbExpression
Project(DbProjectExpression project
)
703 // source.Sort(o).Project(p).Project(p2) -> source.Sort(o).Project(e => p2(p(2)))
704 return ComposeProject(_sort
, _project
, project
);
707 internal override DbExpression
Filter(DbFilterExpression filter
)
709 // source.Sort(o).Project(p).Filter(f) -> source.Filter(e => f(p(e))).Sort(o).Project(p)
710 return RebindProject(
712 ComposeFilter(_source
, _project
, filter
),
717 internal override DbExpression
Limit(DbExpression k
)
719 // the result is already ordered (no compensation is required)
720 return DbExpressionBuilder
.Limit(_root
, k
);
723 internal override DbExpression
Skip(DbExpression k
)
725 // source.Sort(o).Project(p).Skip(k) -> source.Skip(k, o).Project(p)
726 return RebindProject(ApplySortOrderToSkip(_source
, _sort
, k
), _project
);
731 /// Represents an expression for which there is an explicit order by: source.Sort(o)
733 private class SortLifter
: OrderByLifterBase
735 private readonly DbSortExpression _sort
;
736 private readonly DbExpression _source
;
738 internal SortLifter(DbSortExpression sort
, AliasGenerator aliasGenerator
)
739 : base(sort
, aliasGenerator
)
742 _source
= sort
.Input
.Expression
;
745 internal override DbExpression
Project(DbProjectExpression project
)
747 // the result is already ordered (no compensation is required)
751 internal override DbExpression
Filter(DbFilterExpression filter
)
753 // source.Sort(o).Filter(f) -> source.Filter(f).Sort(o)
754 return RebindSort(RebindFilter(_source
, filter
), _sort
);
757 internal override DbExpression
Limit(DbExpression k
)
759 // the result is already ordered (no compensation is required)
760 return DbExpressionBuilder
.Limit(_root
, k
);
763 internal override DbExpression
Skip(DbExpression k
)
765 // source.Sort(o).Skip(k) -> source.Skip(k, o)
766 return ApplySortOrderToSkip(_source
, _sort
, k
);
771 /// Used for sources that do not have any intrinsic order.
773 private class PassthroughOrderByLifter
: OrderByLifterBase
775 internal PassthroughOrderByLifter(DbExpression source
, AliasGenerator aliasGenerator
)
776 : base(source
, aliasGenerator
)
780 internal override DbExpression
Project(DbProjectExpression project
)
785 internal override DbExpression
Filter(DbFilterExpression filter
)
790 internal override DbExpression
OfType(TypeUsage type
)
792 return DbExpressionBuilder
.OfType(_root
, type
);
795 internal override DbExpression
Limit(DbExpression k
)
797 return DbExpressionBuilder
.Limit(_root
, k
);
800 internal override DbExpression
Skip(DbExpression k
)
802 // since the source has no intrinsic order, we need to throw (skip
804 throw EntityUtil
.NotSupported(System
.Data
.Entity
.Strings
.ELinq_SkipWithoutOrder
);