Skip to content

Commit e89fa65

Browse files
committed
Translation imrovements.
- Translator.Visit ignores expressions marked non-visitable. - GetEntityFields improved to not use additional Conditional expression wrapping already existing expression - both methods get IReadOnlyList instead of IEnumerable, it gives opportunity to use count of items to initialize capacity of Lists and not waste memory and time on resize of inner arrays
1 parent a688cf7 commit e89fa65

File tree

2 files changed

+66
-35
lines changed

2 files changed

+66
-35
lines changed

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

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ protected override Expression VisitTypeIs(TypeBinaryExpression tb)
4343

4444
// Structure
4545
var memberType = expression.GetMemberType();
46-
if (memberType==MemberType.Structure
47-
&& typeof (Structure).IsAssignableFrom(operandType))
46+
if (memberType == MemberType.Structure
47+
&& typeof(Structure).IsAssignableFrom(operandType))
4848
return Expression.Constant(false);
4949

5050
// Entity
51-
if (memberType==MemberType.Entity
52-
&& typeof (IEntity).IsAssignableFrom(operandType)) {
51+
if (memberType == MemberType.Entity
52+
&& typeof(IEntity).IsAssignableFrom(operandType)) {
5353
TypeInfo type = context.Model.Types[operandType];
5454
IEnumerable<int> typeIds = type.GetDescendants(true)
5555
.Union(type.GetImplementors(true))
@@ -73,10 +73,12 @@ protected override Expression VisitTypeIs(TypeBinaryExpression tb)
7373

7474
protected override Expression Visit(Expression e)
7575
{
76-
if (e==null)
76+
if (e == null)
7777
return null;
7878
if (e.IsProjection())
7979
return e;
80+
if (state.NonVisitableExpressions.Contains(e))
81+
return e;
8082
if (context.Evaluator.CanBeEvaluated(e)) {
8183
if (typeof (IQueryable).IsAssignableFrom(e.Type))
8284
return base.Visit(ExpressionEvaluator.Evaluate(e));
@@ -231,6 +233,11 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
231233
left = Visit(Expression.Convert(rightNoCasts, typeToCast));
232234
right = Visit(Expression.Convert(binaryExpression.Left, typeToCast));
233235
}
236+
else if (state.NonVisitableExpressions.Contains(leftNoCasts)
237+
|| state.NonVisitableExpressions.Contains(rightNoCasts)) {
238+
left = binaryExpression.Left;
239+
right = binaryExpression.Right;
240+
}
234241
else {
235242
left = Visit(binaryExpression.Left);
236243
right = Visit(binaryExpression.Right);
@@ -1045,7 +1052,7 @@ private IList<Expression> GetStructureFields(
10451052

10461053
switch (fieldExpression.GetMemberType()) {
10471054
case MemberType.Entity:
1048-
IEnumerable<Type> keyFieldTypes = context
1055+
var keyFieldTypes = context
10491056
.Model
10501057
.Types[fieldExpression.Type]
10511058
.Key
@@ -1066,39 +1073,48 @@ private IList<Expression> GetStructureFields(
10661073
return result;
10671074
}
10681075

1069-
private static IList<Expression> GetEntityFields(Expression expression, IEnumerable<Type> keyFieldTypes)
1076+
private IList<Expression> GetEntityFields(Expression expression, IReadOnlyList<Type> keyFieldTypes)
10701077
{
10711078
expression = expression.StripCasts();
1072-
if (expression is IEntityExpression)
1073-
return GetKeyFields(((IEntityExpression) expression).Key, null);
1074-
1075-
1076-
Expression keyExpression;
1077-
1078-
if (expression.IsNull())
1079-
keyExpression = Expression.Constant(null, KeyType);
1080-
else if (IsConditionalOrWellknown(expression))
1079+
if (expression is IEntityExpression iEntityExpression) {
1080+
return GetKeyFields(iEntityExpression.Key, null);
1081+
}
1082+
if (expression.IsNull()) {
1083+
return GetKeyFields(Expression.Constant(null, KeyType), keyFieldTypes);
1084+
}
1085+
if (IsConditionalOrWellknown(expression)) {
10811086
return keyFieldTypes
10821087
.Select((type, index) => GetConditionalKeyField(expression, type, index))
1083-
.ToList();
1084-
else
1085-
{
1086-
ConstantExpression nullEntityExpression = Expression.Constant(null, expression.Type);
1087-
BinaryExpression isNullExpression = Expression.Equal(expression, nullEntityExpression);
1088-
if (!typeof (IEntity).IsAssignableFrom(expression.Type))
1089-
expression = Expression.Convert(expression, typeof (IEntity));
1090-
keyExpression = Expression.Condition(
1088+
.ToList(keyFieldTypes.Count);
1089+
}
1090+
1091+
var nullEntityExpression = Expression.Constant(null, expression.Type);
1092+
var isNullExpression = Expression.Equal(expression, nullEntityExpression);
1093+
if (!typeof(IEntity).IsAssignableFrom(expression.Type))
1094+
expression = Expression.Convert(expression, typeof(IEntity));
1095+
1096+
var resultList = new List<Expression>(keyFieldTypes.Count);
1097+
for(int i = 0, count = keyFieldTypes.Count; i < count; i++) {
1098+
var keyFieldType = keyFieldTypes[i];
1099+
var baseType = keyFieldType.StripNullable();
1100+
var fieldType = (baseType.IsEnum ? Enum.GetUnderlyingType(baseType) : baseType).ToNullable();
1101+
var keyTupleExpression = Expression.MakeMemberAccess(
1102+
Expression.MakeMemberAccess(expression, WellKnownMembers.IEntityKey), WellKnownMembers.Key.Value);
1103+
var tupleAccess = (Expression) keyTupleExpression.MakeTupleAccess(fieldType, i);
1104+
1105+
var entityNullCheck = Expression.Condition(
10911106
isNullExpression,
1092-
Expression.Constant(null, KeyType),
1093-
Expression.MakeMemberAccess(expression, WellKnownMembers.IEntityKey));
1107+
Expression.Constant(null, keyFieldType.ToNullable()),
1108+
tupleAccess);
1109+
resultList.Add(entityNullCheck);
1110+
_ = state.NonVisitableExpressions.Add(entityNullCheck);
10941111
}
1095-
return GetKeyFields(keyExpression, keyFieldTypes);
1112+
return resultList;
10961113
}
10971114

10981115
private static Expression GetConditionalKeyField(Expression expression, Type keyFieldType, int index)
10991116
{
1100-
var ce = expression as ConditionalExpression;
1101-
if (ce != null)
1117+
if (expression is ConditionalExpression ce)
11021118
return Expression.Condition(
11031119
ce.Test,
11041120
GetConditionalKeyField(ce.IfTrue, keyFieldType, index),
@@ -1109,16 +1125,16 @@ private static Expression GetConditionalKeyField(Expression expression, Type key
11091125
return ee.Key.KeyFields[index].LiftToNullable();
11101126
}
11111127

1112-
private static IList<Expression> GetKeyFields(Expression expression, IEnumerable<Type> keyFieldTypes)
1128+
private IList<Expression> GetKeyFields(Expression expression, IEnumerable<Type> keyFieldTypes)
11131129
{
11141130
expression = expression.StripCasts();
11151131

1116-
var keyExpression = expression as KeyExpression;
1117-
if (keyExpression!=null)
1132+
if (expression is KeyExpression keyExpression) {
11181133
return keyExpression
11191134
.KeyFields
1120-
.Select(fieldExpression => (Expression) fieldExpression)
1121-
.ToList();
1135+
.Cast<Expression>()
1136+
.ToList(keyExpression.KeyFields.Count);
1137+
}
11221138

11231139
if (expression.IsNull())
11241140
return keyFieldTypes
@@ -1137,7 +1153,9 @@ private static IList<Expression> GetKeyFields(Expression expression, IEnumerable
11371153
var tupleAccess = (Expression) keyTupleExpression.MakeTupleAccess(fieldType, index);
11381154
if (fieldType!=resultType)
11391155
tupleAccess = Expression.Convert(tupleAccess, resultType);
1140-
return (Expression) Expression.Condition(isNullExpression, Expression.Constant(null, resultType), tupleAccess);
1156+
var checkForNulls = (Expression) Expression.Condition(isNullExpression, Expression.Constant(null, resultType), tupleAccess);
1157+
_ = state.NonVisitableExpressions.Add(checkForNulls);
1158+
return checkForNulls;
11411159
})
11421160
.ToList();
11431161
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Created: 2010.01.21
66

77
using System;
8+
using System.Collections.Generic;
89
using System.Linq.Expressions;
910
using Xtensive.Core;
1011

@@ -55,6 +56,16 @@ public TranslatorScope(Translator translator, TranslatorState previousState)
5556

5657
public Type TypeOfEntityStoredInKey { get; set; }
5758

59+
60+
/// <summary>
61+
/// Expessions that were constructed during original expression translation
62+
/// and aim to replace original parts so they are avoidable to visit by Linq translator.
63+
/// </summary>
64+
/// <remarks>
65+
/// Not all expression that constructed by us should be skipped when visiting.
66+
/// </remarks>
67+
public HashSet<Expression> NonVisitableExpressions { get; private set; }
68+
5869
public bool JoinLocalCollectionEntity
5970
{
6071
get => (flags & TranslatorStateFlags.JoinLocalCollectionEntity) != 0;
@@ -165,6 +176,7 @@ public TranslatorState(Translator translator)
165176
OuterParameters = Parameters = Array.Empty<ParameterExpression>();
166177
IncludeAlgorithm = IncludeAlgorithm.Auto;
167178
TypeOfEntityStoredInKey = null;
179+
NonVisitableExpressions = new HashSet<Expression>();
168180
}
169181

170182
private TranslatorState(TranslatorState currentState)
@@ -176,6 +188,7 @@ private TranslatorState(TranslatorState currentState)
176188
CurrentLambda = currentState.CurrentLambda;
177189
IncludeAlgorithm = currentState.IncludeAlgorithm;
178190
TypeOfEntityStoredInKey = currentState.TypeOfEntityStoredInKey;
191+
NonVisitableExpressions = currentState.NonVisitableExpressions;
179192
}
180193
}
181194
}

0 commit comments

Comments
 (0)