Skip to content

Commit ed8e13a

Browse files
committed
Improve performance of StructureExpression class
1 parent d2703e1 commit ed8e13a

1 file changed

Lines changed: 60 additions & 46 deletions

File tree

Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,112 +7,128 @@
77
using System;
88
using System.Collections.Generic;
99
using System.Linq.Expressions;
10-
using Xtensive.Collections;
1110
using Xtensive.Core;
1211
using Xtensive.Orm.Model;
1312
using System.Linq;
1413

15-
1614
namespace Xtensive.Orm.Linq.Expressions
1715
{
1816
internal sealed class StructureExpression : ParameterizedExpression,
1917
IPersistentExpression
2018
{
2119
private List<PersistentFieldExpression> fields;
20+
private bool isNullable;
21+
2222
internal Segment<int> Mapping;
23-
public TypeInfo PersistentType { get; private set; }
23+
public TypeInfo PersistentType { get; }
2424

25-
public bool IsNullable { get; set; }
25+
public bool IsNullable => isNullable;
2626

2727
public List<PersistentFieldExpression> Fields
2828
{
29-
get { return fields; }
30-
private set
31-
{
29+
get => fields;
30+
private set {
3231
fields = value;
33-
foreach (var fieldExpression in fields.OfType<FieldExpression>())
32+
foreach (var fieldExpression in fields.OfType<FieldExpression>()) {
3433
fieldExpression.Owner = this;
34+
}
3535
}
3636
}
3737

3838
public Expression Remap(int offset, Dictionary<Expression, Expression> processedExpressions)
3939
{
40-
if (!CanRemap)
40+
if (!CanRemap) {
4141
return this;
42+
}
4243

43-
Expression value;
44-
if (processedExpressions.TryGetValue(this, out value))
44+
if (processedExpressions.TryGetValue(this, out var value)) {
4545
return value;
46+
}
4647

4748
var mapping = new Segment<int>(Mapping.Offset + offset, Mapping.Length);
4849
var result = new StructureExpression(PersistentType, mapping);
4950
processedExpressions.Add(this, result);
50-
var processedFields = Fields
51-
.Select(f => f.Remap(offset, processedExpressions))
52-
.Cast<PersistentFieldExpression>()
53-
.ToList();
51+
var processedFields = new List<PersistentFieldExpression>(fields.Count);
52+
foreach (var field in fields) {
53+
// Do not convert to LINQ. We intentionally avoiding closure creation here
54+
processedFields.Add((PersistentFieldExpression) field.Remap(offset, processedExpressions));
55+
}
5456
result.Fields = processedFields;
55-
result.IsNullable = IsNullable;
57+
result.isNullable = isNullable;
5658
return result;
5759
}
5860

5961

6062
public Expression Remap(int[] map, Dictionary<Expression, Expression> processedExpressions)
6163
{
62-
if (!CanRemap)
64+
if (!CanRemap) {
6365
return this;
66+
}
6467

65-
Expression value;
66-
if (processedExpressions.TryGetValue(this, out value))
68+
if (processedExpressions.TryGetValue(this, out var value)) {
6769
return value;
70+
}
6871

69-
var result = new StructureExpression(PersistentType, default(Segment<int>));
72+
var result = new StructureExpression(PersistentType, default);
7073
processedExpressions.Add(this, result);
71-
var processedFields = Fields
72-
.Select(f => f.Remap(map, processedExpressions))
73-
.Where(f => f != null)
74-
.Cast<PersistentFieldExpression>()
75-
.ToList();
74+
var processedFields = new List<PersistentFieldExpression>(fields.Count);
75+
var offset = int.MaxValue;
76+
foreach (var field in fields) {
77+
var mappedField = (PersistentFieldExpression) field.Remap(map, processedExpressions);
78+
if (mappedField == null) {
79+
continue;
80+
}
81+
82+
var mappingOffset = mappedField.Mapping.Offset;
83+
if (mappingOffset < offset) {
84+
offset = mappingOffset;
85+
}
86+
87+
processedFields.Add(mappedField);
88+
}
89+
7690
if (processedFields.Count == 0) {
7791
processedExpressions[this] = null;
7892
return null;
7993
}
80-
var length = processedFields.Select(f => f.Mapping.Offset).Distinct().Count();
81-
var offset = processedFields.Min(f => f.Mapping.Offset);
82-
result.Mapping = new Segment<int>(offset, length);
83-
result.Fields = processedFields;
84-
result.IsNullable = IsNullable;
94+
95+
result.Mapping = new Segment<int>(offset, processedFields.Count);
96+
result.Fields = processedFields;
97+
result.isNullable = isNullable;
8598
return result;
8699
}
87100

88101
public Expression BindParameter(ParameterExpression parameter, Dictionary<Expression, Expression> processedExpressions)
89102
{
90-
Expression value;
91-
if (processedExpressions.TryGetValue(this, out value))
103+
if (processedExpressions.TryGetValue(this, out var value)) {
92104
return value;
105+
}
93106

94107
var result = new StructureExpression(PersistentType, Mapping);
95108
processedExpressions.Add(this, result);
96-
var processedFields = Fields
97-
.Select(f => f.BindParameter(parameter, processedExpressions))
98-
.Cast<PersistentFieldExpression>()
99-
.ToList();
109+
var processedFields = new List<PersistentFieldExpression>(fields.Count);
110+
foreach (var field in fields) {
111+
// Do not convert to LINQ. We intentionally avoiding closure creation here
112+
processedFields.Add((PersistentFieldExpression) field.BindParameter(parameter, processedExpressions));
113+
}
100114
result.Fields = processedFields;
101115
return result;
102116
}
103117

104118
public Expression RemoveOuterParameter(Dictionary<Expression, Expression> processedExpressions)
105119
{
106-
Expression value;
107-
if (processedExpressions.TryGetValue(this, out value))
120+
if (processedExpressions.TryGetValue(this, out var value)) {
108121
return value;
122+
}
109123

110124
var result = new StructureExpression(PersistentType, Mapping);
111125
processedExpressions.Add(this, result);
112-
var processedFields = Fields
113-
.Select(f => f.RemoveOuterParameter(processedExpressions))
114-
.Cast<PersistentFieldExpression>()
115-
.ToList();
126+
var processedFields = new List<PersistentFieldExpression>(fields.Count);
127+
foreach (var field in fields) {
128+
// Do not convert to LINQ. We intentionally avoiding closure creation here
129+
processedFields.Add((PersistentFieldExpression) field.RemoveOuterParameter(processedExpressions));
130+
}
131+
116132
result.Fields = processedFields;
117133
return result;
118134
}
@@ -127,12 +143,12 @@ public static StructureExpression CreateLocalCollectionStructure(TypeInfo typeIn
127143
var destinationFields = new List<PersistentFieldExpression>(sourceFields.Count);
128144
var result = new StructureExpression(typeInfo, mapping) {Fields = destinationFields};
129145
foreach (var field in sourceFields) {
146+
// Do not convert to LINQ. We intentionally avoiding closure creation here
130147
destinationFields.Add(BuildNestedFieldExpression(field, mapping.Offset));
131148
}
132149
return result;
133150
}
134151

135-
// ReSharper disable RedundantNameQualifier
136152
private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo nestedField, int offset)
137153
{
138154
if (nestedField.IsPrimitive)
@@ -143,8 +159,6 @@ private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo ne
143159
return EntityFieldExpression.CreateEntityField(nestedField, offset);
144160
throw new NotSupportedException(string.Format(Strings.ExNestedFieldXIsNotSupported, nestedField.Attributes));
145161
}
146-
// ReSharper restore RedundantNameQualifier
147-
148162

149163
// Constructors
150164

@@ -153,7 +167,7 @@ private StructureExpression(
153167
in Segment<int> mapping)
154168
: base(ExtendedExpressionType.Structure, persistentType.UnderlyingType, null, false)
155169
{
156-
this.Mapping = mapping;
170+
Mapping = mapping;
157171
PersistentType = persistentType;
158172
}
159173
}

0 commit comments

Comments
 (0)