Skip to content

Commit 6ef8cc8

Browse files
committed
Linq.Translator improvements
1 parent 8b68947 commit 6ef8cc8

4 files changed

Lines changed: 129 additions & 92 deletions

File tree

Orm/Xtensive.Orm/Orm/Internals/WellKnownOrmTypes.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ internal static class WellKnownOrmTypes
3535

3636
public static readonly Type Tuple = typeof(Tuple);
3737

38+
public static readonly Type Session = typeof(Session);
3839
public static readonly Type Query = typeof(Query);
40+
public static readonly Type QueryEndpoint = typeof(QueryEndpoint);
3941
public static readonly Type QueryProvider = typeof(QueryProvider);
4042

4143
public static readonly Type TranslatedQuery = typeof(TranslatedQuery);

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

Lines changed: 90 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Created: 2009.02.27
66

77
using System;
8+
using System.Collections.Concurrent;
89
using System.Collections.Generic;
910
using System.Linq;
1011
using System.Linq.Expressions;
@@ -33,39 +34,44 @@ namespace Xtensive.Orm.Linq
3334
{
3435
internal sealed partial class Translator
3536
{
37+
private static readonly Type OrmCollectionExtensionsType = typeof(CollectionExtensionsEx);
38+
private static readonly Type OrmQueryableExtensionsType = typeof(QueryableExtensions);
39+
3640
protected override Expression VisitTypeIs(TypeBinaryExpression tb)
3741
{
3842
var expression = tb.Expression;
3943
Type expressionType = expression.Type;
4044
Type operandType = tb.TypeOperand;
41-
if (operandType.IsAssignableFrom(expressionType))
45+
if (operandType.IsAssignableFrom(expressionType)) {
4246
return Expression.Constant(true);
47+
}
4348

4449
// Structure
4550
var memberType = expression.GetMemberType();
46-
if (memberType == MemberType.Structure
47-
&& WellKnownOrmTypes.Structure.IsAssignableFrom(operandType))
51+
if (memberType == MemberType.Structure && WellKnownOrmTypes.Structure.IsAssignableFrom(operandType)) {
4852
return Expression.Constant(false);
53+
}
4954

5055
// Entity
51-
if (memberType == MemberType.Entity
52-
&& WellKnownOrmInterfaces.Entity.IsAssignableFrom(operandType)) {
53-
TypeInfo type = context.Model.Types[operandType];
54-
IEnumerable<int> typeIds = type.GetDescendants(true)
55-
.Union(type.GetImplementors(true))
56-
.Union(Enumerable.Repeat(type, 1))
56+
if (memberType == MemberType.Entity && WellKnownOrmInterfaces.Entity.IsAssignableFrom(operandType)) {
57+
var entityTypeInfo = context.Model.Types[operandType];
58+
var typeIds = entityTypeInfo.GetDescendants(true)
59+
.Union(entityTypeInfo.GetImplementors(true))
60+
.Union(Enumerable.Repeat(entityTypeInfo, 1))
5761
.Select(t => context.TypeIdRegistry.GetTypeId(t));
58-
MemberExpression memberExpression = Expression.MakeMemberAccess(expression, WellKnownMembers.TypeId);
59-
Expression boolExpression = null;
60-
foreach (int typeId in typeIds)
61-
boolExpression = MakeBooleanExpression(
62-
boolExpression,
63-
memberExpression,
62+
63+
var typeIdPropAccessor = Expression.MakeMemberAccess(expression, WellKnownMembers.TypeId);
64+
Expression filterByTypeIdExpression = null;
65+
foreach (var typeId in typeIds) {
66+
filterByTypeIdExpression = MakeBooleanExpression(
67+
filterByTypeIdExpression,
68+
typeIdPropAccessor,
6469
Expression.Constant(typeId),
6570
ExpressionType.Equal,
6671
ExpressionType.OrElse);
72+
}
6773

68-
return Visit(boolExpression);
74+
return Visit(filterByTypeIdExpression);
6975
}
7076

7177
throw new NotSupportedException(Strings.ExTypeIsMethodSupportsOnlyEntitiesAndStructures);
@@ -101,12 +107,12 @@ protected override Expression VisitUnary(UnaryExpression u)
101107
{
102108
switch (u.NodeType) {
103109
case ExpressionType.TypeAs:
104-
if (u.GetMemberType()==MemberType.Entity)
110+
if (u.GetMemberType() is MemberType.Entity)
105111
return VisitTypeAs(u.Operand, u.Type);
106112
break;
107113
case ExpressionType.Convert:
108114
case ExpressionType.ConvertChecked:
109-
if (u.GetMemberType()==MemberType.Entity) {
115+
if (u.GetMemberType() is MemberType.Entity) {
110116
if (u.Type==u.Operand.Type
111117
|| u.Type.IsAssignableFrom(u.Operand.Type)
112118
|| !WellKnownOrmInterfaces.Entity.IsAssignableFrom(u.Operand.Type))
@@ -134,9 +140,9 @@ protected override Expression VisitLambda(LambdaExpression le)
134140
body = Visit(body);
135141
ParameterExpression parameter = le.Parameters[0];
136142
var shouldTranslate =
137-
(body.NodeType!=ExpressionType.New || body.IsNewExpressionSupportedByStorage())
138-
&& body.NodeType!=ExpressionType.MemberInit
139-
&& !(body.NodeType==ExpressionType.Constant && state.BuildingProjection);
143+
(body.NodeType is not ExpressionType.New || body.IsNewExpressionSupportedByStorage())
144+
&& body.NodeType is not ExpressionType.MemberInit
145+
&& !(body.NodeType is ExpressionType.Constant && state.BuildingProjection);
140146
if (shouldTranslate)
141147
body = body.IsProjection()
142148
? BuildSubqueryResult((ProjectionExpression) body, le.Body.Type)
@@ -177,22 +183,22 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
177183
left = ExpressionEvaluator.Evaluate(binaryExpression.Left);
178184
}
179185
else {
180-
var leftMemberAccess = binaryExpression.Left as MemberExpression;
181-
left = leftMemberAccess!=null && leftMemberAccess.Member.ReflectedType.IsClosure()
186+
left = (binaryExpression.Left is MemberExpression leftMemberAccess
187+
&& leftMemberAccess.Member.ReflectedType.IsClosure())
182188
? ExpressionEvaluator.Evaluate(leftMemberAccess)
183189
: Visit(binaryExpression.Left);
184190
}
185191
if (context.Evaluator.CanBeEvaluated(binaryExpression.Right)) {
186192
right = ExpressionEvaluator.Evaluate(binaryExpression.Right);
187193
}
188194
else {
189-
var rightMemberAccess = binaryExpression.Right as MemberExpression;
190-
right = rightMemberAccess!=null && rightMemberAccess.Member.ReflectedType.IsClosure()
195+
right = (binaryExpression.Right is MemberExpression rightMemberAccess
196+
&& rightMemberAccess.Member.ReflectedType.IsClosure())
191197
? ExpressionEvaluator.Evaluate(rightMemberAccess)
192198
: Visit(binaryExpression.Right);
193199
}
194200
}
195-
else if (memberType == MemberType.Entity || memberType == MemberType.Structure) {
201+
else if (memberType is MemberType.Entity or MemberType.Structure) {
196202
if (binaryExpression.NodeType == ExpressionType.Coalesce) {
197203
if ((context.Evaluator.CanBeEvaluated(binaryExpression.Right) && !(binaryExpression.Right is ConstantExpression))
198204
|| (context.Evaluator.CanBeEvaluated(binaryExpression.Left) && !(binaryExpression.Left is ConstantExpression)))
@@ -219,14 +225,14 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
219225
var rightNoCastsType = rightNoCasts.Type;
220226
var bareRightType = rightNoCastsType.StripNullable();
221227

222-
if (bareLeftType.IsEnum && rightNoCasts.NodeType == ExpressionType.Constant) {
228+
if (bareLeftType.IsEnum && rightNoCasts.NodeType is ExpressionType.Constant) {
223229
var typeToCast = leftNoCastsType.IsNullable()
224230
? bareLeftType.GetEnumUnderlyingType().ToNullable()
225231
: leftNoCastsType.GetEnumUnderlyingType();
226232
left = Visit(Expression.Convert(leftNoCasts, typeToCast));
227233
right = Visit(Expression.Convert(binaryExpression.Right, typeToCast));
228234
}
229-
else if (bareRightType.IsEnum && leftNoCasts.NodeType == ExpressionType.Constant) {
235+
else if (bareRightType.IsEnum && leftNoCasts.NodeType is ExpressionType.Constant) {
230236
var typeToCast = rightNoCastsType.IsNullable()
231237
? bareRightType.GetEnumUnderlyingType().ToNullable()
232238
: rightNoCastsType.GetEnumUnderlyingType();
@@ -253,15 +259,15 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
253259
binaryExpression.IsLiftedToNull,
254260
binaryExpression.Method);
255261

256-
if (binaryExpression.NodeType==ExpressionType.Equal
257-
|| binaryExpression.NodeType==ExpressionType.NotEqual)
262+
if (binaryExpression.NodeType is ExpressionType.Equal or ExpressionType.NotEqual)
258263
return VisitBinaryRecursive(resultBinaryExpression, binaryExpression);
259264

260-
if (binaryExpression.NodeType==ExpressionType.ArrayIndex) {
261-
var newArrayExpression = left.StripCasts() as NewArrayExpression;
262-
var indexExpression = right.StripCasts() as ConstantExpression;
263-
if (newArrayExpression!=null && indexExpression!=null && indexExpression.Type==WellKnownTypes.Int32)
265+
if (binaryExpression.NodeType is ExpressionType.ArrayIndex) {
266+
if (left.StripCasts() is NewArrayExpression newArrayExpression
267+
&& right.StripCasts() is ConstantExpression indexExpression
268+
&& indexExpression.Type == WellKnownTypes.Int32) {
264269
return newArrayExpression.Expressions[(int) indexExpression.Value];
270+
}
265271

266272
throw new NotSupportedException(String.Format(Strings.ExBinaryExpressionXOfTypeXIsNotSupported, binaryExpression.ToString(true), binaryExpression.NodeType));
267273
}
@@ -271,9 +277,9 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
271277
static bool EnumRewritableOperations(BinaryExpression b)
272278
{
273279
var nt = b.NodeType;
274-
return nt == ExpressionType.Equal || nt == ExpressionType.NotEqual
275-
|| nt == ExpressionType.GreaterThan || nt == ExpressionType.GreaterThanOrEqual
276-
|| nt == ExpressionType.LessThan || nt == ExpressionType.LessThanOrEqual;
280+
return nt is ExpressionType.Equal or ExpressionType.NotEqual
281+
or ExpressionType.GreaterThan or ExpressionType.GreaterThanOrEqual
282+
or ExpressionType.LessThan or ExpressionType.LessThanOrEqual;
277283
}
278284
}
279285

@@ -283,19 +289,20 @@ protected override Expression VisitConditional(ConditionalExpression c)
283289
? c.IfFalse.GetMemberType()
284290
: c.IfTrue.GetMemberType();
285291
if (memberType == MemberType.Entity || memberType == MemberType.Structure) {
286-
if ((context.Evaluator.CanBeEvaluated(c.IfFalse) && !(c.IfFalse is ConstantExpression))
287-
|| (context.Evaluator.CanBeEvaluated(c.IfTrue) && !(c.IfTrue is ConstantExpression)))
292+
if ((context.Evaluator.CanBeEvaluated(c.IfFalse) && (c.IfFalse is not ConstantExpression))
293+
|| (context.Evaluator.CanBeEvaluated(c.IfTrue) && (c.IfTrue is not ConstantExpression)))
288294
throw new NotSupportedException(string.Format(Strings.ExXExpressionsWithConstantValuesOfYTypeNotSupported, "Conditional", memberType.ToString()));
289295
}
290296
return base.VisitConditional(c);
291297
}
292298

293299
private Expression ConvertEnum(Expression left)
294300
{
295-
var underlyingType = Enum.GetUnderlyingType(left.Type.StripNullable());
296-
if (left.Type.IsNullable())
297-
underlyingType = underlyingType.ToNullable();
298-
left = left.NodeType==ExpressionType.Convert
301+
var underlyingType = left.Type.IsNullable()
302+
? Enum.GetUnderlyingType(left.Type.GetGenericArguments()[0]).ToNullable()
303+
: Enum.GetUnderlyingType(left.Type);
304+
305+
left = left.NodeType is ExpressionType.Convert
299306
? Expression.Convert(((UnaryExpression) left).Operand, underlyingType)
300307
: Expression.Convert(left, underlyingType);
301308
return left;
@@ -450,7 +457,7 @@ protected override Expression VisitMethodCall(MethodCallExpression mc)
450457
throw new InvalidOperationException(String.Format(Strings.ExMethodCallExpressionXIsNotSupported, mc.ToString(true)));
451458
}
452459
// Visit QueryEndpoint.
453-
if (method.DeclaringType == typeof(QueryEndpoint)) {
460+
if (method.DeclaringType == WellKnownOrmTypes.QueryEndpoint) {
454461
// Query.All<T>
455462
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.All)) {
456463
return ConstructQueryable(mc);
@@ -1208,22 +1215,25 @@ private ColumnExpression AddCalculatedColumn(ParameterExpression sourceParameter
12081215
var dataSource = oldResult.ItemProjector.DataSource;
12091216

12101217
SortProvider sortProvider = null;
1211-
if (dataSource is SortProvider) {
1212-
sortProvider = (SortProvider) dataSource;
1213-
dataSource = sortProvider.Source;
1218+
if (dataSource is SortProvider sortProvider1) {
1219+
sortProvider = sortProvider1;
1220+
dataSource = sortProvider1.Source;
12141221
}
12151222

1216-
var columns = new List<CalculatedColumnDescriptor>();
1217-
if (state.AllowCalculableColumnCombine && dataSource is CalculateProvider && isInlined==((CalculateProvider) dataSource).IsInlined) {
1218-
var calculateProvider = ((CalculateProvider) dataSource);
1219-
var presentColumns = calculateProvider
1223+
CalculatedColumnDescriptor[] columns;
1224+
if (state.AllowCalculableColumnCombine && dataSource is CalculateProvider calculateProvider && isInlined==calculateProvider.IsInlined) {
1225+
columns = calculateProvider
12201226
.CalculatedColumns
1221-
.Select(cc => new CalculatedColumnDescriptor(cc.Name, cc.Type, cc.Expression));
1222-
columns.AddRange(presentColumns);
1227+
.Select(cc => new CalculatedColumnDescriptor(cc.Name, cc.Type, cc.Expression))
1228+
.ToArray(calculateProvider.CalculatedColumns.Length + 1);
1229+
columns[^1] = descriptor;
12231230
dataSource = calculateProvider.Source;
12241231
}
1225-
columns.Add(descriptor);
1226-
dataSource = dataSource.Calculate(isInlined, columns.ToArray());
1232+
else {
1233+
columns = new[] { descriptor };
1234+
}
1235+
1236+
dataSource = dataSource.Calculate(isInlined, columns);
12271237

12281238
if (sortProvider!=null)
12291239
dataSource = dataSource.OrderBy(sortProvider.Order);
@@ -1291,37 +1301,44 @@ private static IList<Expression> GetAnonymousArguments(Expression expression, Ty
12911301
.Select((methodInfo, index) => new {methodInfo.Name, Argument = newExpression.Arguments[index]})
12921302
.OrderBy(a => a.Name)
12931303
.Select(a => a.Argument);
1294-
return arguments.ToList();
1304+
return arguments.ToList(newExpression.Members.Count);
12951305
}
12961306

12971307
if (expression.NodeType==ExpressionType.Constant) {
12981308
var constantExpression = expression as ConstantExpression;
12991309
if (constantExpression.Value==null && constantExpression.Type==WellKnownTypes.Object) {
13001310
var newConstantExpressionType = anonymousTypeForNullValues ?? constantExpression.Type;
13011311
constantExpression = Expression.Constant(null, newConstantExpressionType);
1302-
return constantExpression
1312+
var orderedProps1 = constantExpression
13031313
.Type
1304-
.GetProperties()
1305-
.OrderBy(property => property.Name)
1306-
.Select(p => Expression.MakeMemberAccess(constantExpression, p))
1307-
.Cast<Expression>()
1308-
.ToList();
1314+
.GetProperties();
1315+
Array.Sort(orderedProps1, CompareProps);
1316+
1317+
return orderedProps1
1318+
.Select(p => (Expression) Expression.MakeMemberAccess(constantExpression, p))
1319+
.ToList(orderedProps1.Length);
13091320
}
13101321
}
13111322

1312-
return expression
1323+
var orderedProps = expression
13131324
.Type
1314-
.GetProperties()
1315-
.OrderBy(property => property.Name)
1316-
.Select(p => Expression.MakeMemberAccess(expression, p))
1317-
.Select(e => (Expression) e)
1318-
.ToList();
1325+
.GetProperties();
1326+
Array.Sort(orderedProps, CompareProps);
1327+
1328+
return orderedProps
1329+
.Select(p => (Expression) Expression.MakeMemberAccess(expression, p))
1330+
.ToList(orderedProps.Length);
1331+
1332+
static int CompareProps(PropertyInfo p1, PropertyInfo p2)
1333+
{
1334+
return StringComparer.Ordinal.Compare(p1.Name, p2.Name);
1335+
}
13191336
}
13201337

13211338
protected override Expression VisitMemberInit(MemberInitExpression mi)
13221339
{
13231340
var newExpression = mi.NewExpression;
1324-
var arguments = VisitNewExpressionArguments(newExpression);
1341+
VisitNewExpressionArgumentsSkipResults(newExpression);
13251342
var bindings = VisitBindingList(mi.Bindings).Cast<MemberAssignment>();
13261343
var constructorExpression = (ConstructorExpression) VisitNew(mi.NewExpression);
13271344
foreach (var binding in bindings) {
@@ -1529,13 +1546,12 @@ private Expression GetMemberWithRemap(Expression expression, MemberInfo memberIn
15291546

15301547
private Expression GetConditionalMember(Expression expression, MemberInfo member, Expression sourceExpression)
15311548
{
1532-
var ce = expression as ConditionalExpression;
1533-
if (ce != null) {
1549+
if (expression is ConditionalExpression ce) {
15341550
var ifTrue = GetConditionalMember(ce.IfTrue, member, sourceExpression);
15351551
var ifFalse = GetConditionalMember(ce.IfFalse, member, sourceExpression);
15361552
if (ifTrue == null || ifFalse == null)
15371553
return null;
1538-
return Expression.Condition(ce.Test,ifTrue,ifFalse);
1554+
return Expression.Condition(ce.Test, ifTrue, ifFalse);
15391555
}
15401556
if (expression.IsNull()) {
15411557
var mt = member.MemberType;
@@ -1648,8 +1664,7 @@ private void EnsureEntityFieldsAreJoined(EntityExpression entityExpression)
16481664
public void EnsureEntityFieldsAreJoined(EntityExpression entityExpression, ItemProjectorExpression itemProjector)
16491665
{
16501666
TypeInfo typeInfo = entityExpression.PersistentType;
1651-
if (
1652-
typeInfo.Fields.All(fieldInfo => entityExpression.Fields.Any(entityField => entityField.Name==fieldInfo.Name)))
1667+
if (typeInfo.Fields.All(fieldInfo => entityExpression.Fields.Any(entityField => entityField.Name==fieldInfo.Name)))
16531668
return; // All fields are already joined
16541669
IndexInfo joinedIndex = typeInfo.Indexes.PrimaryIndex;
16551670
var joinedRs = joinedIndex.GetQuery().Alias(itemProjector.Context.GetNextAlias());
@@ -1807,7 +1822,7 @@ private MemberInfo TryGetActualPropertyInfo(PropertyInfo propertyInfo, Type init
18071822
private IList<ColumnInfo> GetColumnsToSearch(ConstantExpression arrayOfColumns, Type elementType, TypeInfo domainType)
18081823
{
18091824
var columnAccessLambdas = (LambdaExpression[])arrayOfColumns.Value;
1810-
var fulltextFields = new List<ColumnInfo>();
1825+
var fulltextFields = new List<ColumnInfo>(columnAccessLambdas.Length);
18111826
foreach (var lambda in columnAccessLambdas) {
18121827
var field = FieldExtractor.Extract(lambda, elementType, domainType);
18131828
if (field.Column==null)

0 commit comments

Comments
 (0)