Skip to content

Commit 6811886

Browse files
committed
Restrain ability to skip member expressions
Expressions' visit can be skipped only in order by or group by and for less expression types
1 parent 5b8999c commit 6811886

File tree

4 files changed

+62
-28
lines changed

4 files changed

+62
-28
lines changed

Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2008-2024 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Kochetov
55
// Created: 2008.12.02
66

@@ -89,6 +89,11 @@ private static bool IsForeignQuery(Expression expression)
8989
return false;
9090
}
9191

92+
public static bool IsExtendedExpression(this Expression expression)
93+
{
94+
return (ExtendedExpressionType) expression.StripMarkers().NodeType >= ExtendedExpressionType.Projection;
95+
}
96+
9297
public static bool IsItemProjector(this Expression expression)
9398
{
9499
expression = expression.StripMarkers();
@@ -151,12 +156,16 @@ public static Type GetGroupingKeyType(this Expression expression)
151156

152157
public static Expression StripMarkers(this Expression e)
153158
{
154-
var ee = e as ExtendedExpression;
155-
if (ee!=null && ee.ExtendedType==ExtendedExpressionType.Marker) {
156-
var marker = (MarkerExpression) ee;
157-
return marker.Target;
159+
if ((ExtendedExpressionType)e.NodeType==ExtendedExpressionType.Marker) {
160+
return ((MarkerExpression) e).Target;
158161
}
159162
return e;
163+
//var ee = e as ExtendedExpression;
164+
//if (ee!=null && ee.ExtendedType==ExtendedExpressionType.Marker) {
165+
// var marker = (MarkerExpression) ee;
166+
// return marker.Target;
167+
//}
168+
//return e;
160169
}
161170

162171
public static bool IsMarker(this Expression e)

Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,26 +1343,41 @@ private Expression GetMember(Expression expression, MemberInfo member, Expressio
13431343
if (memberIndex < 0)
13441344
throw new InvalidOperationException(string.Format(Strings.ExCouldNotGetMemberXFromExpression, member));
13451345
var argument = newExpression.Arguments[memberIndex];
1346-
var currentLambda = state.CurrentLambda;
1347-
if (!(currentLambda is null) && context.Bindings.TryGetValue(currentLambda.Parameters[0], out var projection)) {
1348-
// Here, we try to detect whether the expression has already visited, in such cases
1349-
// re-visiting it may cause issues. For instance, .OrderBy(x=>x.SomeField) translation
1350-
// gets projection of already visited source and substitutes parameter 'x' access with
1351-
// that projection. For now TranslatorState.ShouldOmitConvertToObject == true, is a marker
1352-
// of OrderBy visiting, because it is the only place that sets 'true' to the property,
1353-
// but we can't rely on it for future-proof solution.
1354-
1355-
// Knowing that parameter-to-projection binding happens when expression has visited,
1356-
// we assume this check is enough for certain cases, probably not for all of them.
1357-
// We can't deny visiting of the argument for all the cases because of chance to break
1358-
// some cases we could not imagine at the time of changes
1359-
switch (argument.NodeType) {
1360-
case ExpressionType.TypeAs:
1361-
case ExpressionType.Convert:
1362-
case ExpressionType.ConvertChecked:
1363-
if (argument.Type == typeof(object) && state.ShouldOmitConvertToObject)
1364-
argument = argument.StripCasts();
1365-
break;
1346+
1347+
if (state.GroupingKey || state.OrderingKey) {
1348+
var requireVisit = !argument.IsExtendedExpression() && argument.NodeType switch {
1349+
ExpressionType.New => true,
1350+
ExpressionType.Call => true,
1351+
ExpressionType.Coalesce => true,
1352+
ExpressionType.Conditional => true,
1353+
_ => false
1354+
};
1355+
1356+
var currentLambda = state.CurrentLambda;
1357+
if (!(currentLambda is null) && context.Bindings.TryGetValue(currentLambda.Parameters[0], out var projection)
1358+
&& !requireVisit) {
1359+
// Here, we try to detect whether the expression has already visited, in such cases
1360+
// re-visiting it may cause issues. For instance, .OrderBy(x=>x.SomeField) translation
1361+
// gets projection of already visited source and substitutes parameter 'x' access with
1362+
// that projection. For now TranslatorState.ShouldOmitConvertToObject == true, is a marker
1363+
// of OrderBy visiting, because it is the only place that sets 'true' to the property,
1364+
// but we can't rely on it for future-proof solution.
1365+
1366+
// Knowing that parameter-to-projection binding happens when expression has visited,
1367+
// we assume this check is enough for certain cases, probably not for all of them.
1368+
// We can't deny visiting of the argument for all the cases because of chance to break
1369+
// some cases we could not imagine at the time of changes
1370+
switch (argument.NodeType) {
1371+
case ExpressionType.TypeAs:
1372+
case ExpressionType.Convert:
1373+
case ExpressionType.ConvertChecked:
1374+
if (argument.Type == typeof(object) && state.ShouldOmitConvertToObject)
1375+
argument = argument.StripCasts();
1376+
break;
1377+
}
1378+
}
1379+
else {
1380+
argument = Visit(argument);
13661381
}
13671382
}
13681383
else {

Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,7 @@ private Expression VisitSort(Expression expression)
988988
using (state.CreateScope()) {
989989
state.ShouldOmitConvertToObject = true;
990990
state.CalculateExpressions = true;
991+
state.OrderingKey = true;
991992
var orderByProjector = (ItemProjectorExpression) VisitLambda(sortExpression);
992993
var columns = orderByProjector
993994
.GetColumns(ColumnExtractionModes.TreatEntityAsKey | ColumnExtractionModes.Distinct);

Orm/Xtensive.Orm/Orm/Linq/TranslatorState.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ private enum TranslatorStateFlags
2525
ShouldOmitConvertToObject = 1 << 6,
2626
RequestCalculateExpressions = 1 << 7,
2727
RequestCalculateExpressionsOnce = 1 << 8,
28-
SkipNullableColumnsDetectionInGroupBy = 1 << 9
28+
SkipNullableColumnsDetectionInGroupBy = 1 << 9,
29+
IsOrderingKey = 1 << 10,
2930
}
3031

3132
internal readonly ref struct TranslatorScope
@@ -105,6 +106,14 @@ public bool GroupingKey
105106
: flags & ~TranslatorStateFlags.IsGroupingKey;
106107
}
107108

109+
public bool OrderingKey
110+
{
111+
get => (flags & TranslatorStateFlags.IsOrderingKey) != 0;
112+
set => flags = value
113+
? flags | TranslatorStateFlags.IsOrderingKey
114+
: flags & ~TranslatorStateFlags.IsOrderingKey;
115+
}
116+
108117
public bool IsTailMethod
109118
{
110119
get => (flags & TranslatorStateFlags.IsTailMethod) != 0;

0 commit comments

Comments
 (0)