Skip to content

Commit 48c81bd

Browse files
authored
Merge pull request #41 from servicetitan/expression-performance
Improves performance of Expressions
2 parents a4fff45 + ac43303 commit 48c81bd

File tree

7 files changed

+625
-502
lines changed

7 files changed

+625
-502
lines changed

Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs

Lines changed: 107 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2009-2020 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: Alexis Kochetov
55
// Created: 2009.05.05
66

77
using System;
8-
using System.Diagnostics;
98
using System.Linq;
109
using System.Collections.Generic;
1110
using System.Linq.Expressions;
12-
using Xtensive.Core;
1311
using Xtensive.Orm.Model;
1412

1513
namespace Xtensive.Orm.Linq.Expressions
@@ -19,94 +17,115 @@ internal class EntityExpression : ParameterizedExpression,
1917
{
2018
private List<PersistentFieldExpression> fields;
2119

22-
public TypeInfo PersistentType { get; private set; }
20+
public TypeInfo PersistentType { get; }
2321

24-
public KeyExpression Key { get; private set; }
22+
public KeyExpression Key { get; }
2523

2624
public List<PersistentFieldExpression> Fields
2725
{
28-
get { return fields; }
29-
private set
30-
{
26+
get => fields;
27+
private set {
3128
fields = value;
32-
var fieldExpressions = fields.OfType<FieldExpression>();
33-
foreach (var fieldExpression in fieldExpressions)
29+
foreach (var fieldExpression in fields.OfType<FieldExpression>()) {
3430
fieldExpression.Owner = this;
31+
}
3532
}
3633
}
3734

3835
public bool IsNullable { get; set; }
3936

4037
public Expression Remap(int offset, Dictionary<Expression, Expression> processedExpressions)
4138
{
42-
if (!CanRemap)
39+
if (!CanRemap) {
4340
return this;
44-
Expression value;
45-
if (processedExpressions.TryGetValue(this, out value))
41+
}
42+
43+
if (processedExpressions.TryGetValue(this, out var value)) {
4644
return value;
45+
}
4746

4847
var keyExpression = (KeyExpression) Key.Remap(offset, processedExpressions);
4948
var result = new EntityExpression(PersistentType, keyExpression, OuterParameter, DefaultIfEmpty);
5049
processedExpressions.Add(this, result);
5150
result.IsNullable = IsNullable;
52-
result.Fields = Fields
53-
.Select(f => f.Remap(offset, processedExpressions))
54-
.Cast<PersistentFieldExpression>()
55-
.ToList();
51+
var processedFields = new List<PersistentFieldExpression>(fields.Count);
52+
foreach (var field in fields) {
53+
// Do not convert to LINQ. We want to avoid a closure creation here.
54+
processedFields.Add((PersistentFieldExpression) field.Remap(offset, processedExpressions));
55+
}
56+
57+
result.Fields = processedFields;
5658
return result;
5759
}
5860

5961
public Expression Remap(int[] map, Dictionary<Expression, Expression> processedExpressions)
6062
{
61-
if (!CanRemap)
63+
if (!CanRemap) {
6264
return this;
63-
Expression value;
64-
if (processedExpressions.TryGetValue(this, out value))
65+
}
66+
67+
if (processedExpressions.TryGetValue(this, out var value)) {
6568
return value;
69+
}
6670

6771
var keyExpression = (KeyExpression) Key.Remap(map, processedExpressions);
68-
if (keyExpression==null)
72+
if (keyExpression == null) {
6973
return null;
74+
}
75+
7076
var result = new EntityExpression(PersistentType, keyExpression, OuterParameter, DefaultIfEmpty);
7177
processedExpressions.Add(this, result);
7278
result.IsNullable = IsNullable;
73-
result.Fields = Fields
74-
.Select(f => f.Remap(map, processedExpressions))
75-
.Where(f => f!=null)
76-
.Cast<PersistentFieldExpression>()
77-
.ToList();
79+
var processedFields = new List<PersistentFieldExpression>(fields.Count);
80+
foreach (var field in fields) {
81+
// Do not convert to LINQ. We want to avoid a closure creation here.
82+
var mappedField = (PersistentFieldExpression) field.Remap(map, processedExpressions);
83+
if (mappedField == null) {
84+
continue;
85+
}
86+
87+
processedFields.Add(mappedField);
88+
}
89+
90+
result.Fields = processedFields;
7891
return result;
7992
}
8093

8194
public Expression BindParameter(ParameterExpression parameter, Dictionary<Expression, Expression> processedExpressions)
8295
{
83-
Expression value;
84-
if (processedExpressions.TryGetValue(this, out value))
96+
if (processedExpressions.TryGetValue(this, out var value)) {
8597
return value;
98+
}
8699

87100
var keyExpression = (KeyExpression) Key.BindParameter(parameter, processedExpressions);
88101
var result = new EntityExpression(PersistentType, keyExpression, parameter, DefaultIfEmpty);
89102
processedExpressions.Add(this, result);
90-
result.Fields = Fields
91-
.Select(f => f.BindParameter(parameter, processedExpressions))
92-
.Cast<PersistentFieldExpression>()
93-
.ToList();
103+
var processedFields = new List<PersistentFieldExpression>(fields.Count);
104+
foreach (var field in fields) {
105+
// Do not convert to LINQ. We want to avoid a closure creation here.
106+
processedFields.Add((PersistentFieldExpression) field.BindParameter(parameter, processedExpressions));
107+
}
108+
109+
result.Fields = processedFields;
94110
return result;
95111
}
96112

97113
public Expression RemoveOuterParameter(Dictionary<Expression, Expression> processedExpressions)
98114
{
99-
Expression value;
100-
if (processedExpressions.TryGetValue(this, out value))
115+
if (processedExpressions.TryGetValue(this, out var value)) {
101116
return value;
117+
}
102118

103119
var keyExpression = (KeyExpression) Key.RemoveOuterParameter(processedExpressions);
104120
var result = new EntityExpression(PersistentType, keyExpression, null, DefaultIfEmpty);
105121
processedExpressions.Add(this, result);
106-
result.Fields = Fields
107-
.Select(f => f.RemoveOuterParameter(processedExpressions))
108-
.Cast<PersistentFieldExpression>()
109-
.ToList();
122+
var processedFields = new List<PersistentFieldExpression>(fields.Count);
123+
foreach (var field in fields) {
124+
// Do not convert to LINQ. We want to avoid a closure creation here.
125+
processedFields.Add((PersistentFieldExpression) field.RemoveOuterParameter(processedExpressions));
126+
}
127+
128+
result.Fields = processedFields;
110129
return result;
111130
}
112131

@@ -118,70 +137,88 @@ public static void Fill(EntityExpression entityExpression, int offset)
118137
var typeInfo = entityExpression.PersistentType;
119138
foreach (var nestedField in typeInfo.Fields.Except(entityExpression.Fields.OfType<FieldExpression>().Select(field=>field.Field))) {
120139
var nestedFieldExpression = BuildNestedFieldExpression(nestedField, offset);
121-
var fieldExpression = nestedFieldExpression as FieldExpression;
122-
if (fieldExpression!=null)
140+
if (nestedFieldExpression is FieldExpression fieldExpression) {
123141
fieldExpression.Owner = entityExpression;
142+
}
143+
124144
entityExpression.fields.Add(nestedFieldExpression);
125145
}
126146
}
127147

128148
public static EntityExpression Create(TypeInfo typeInfo, int offset, bool keyFieldsOnly)
129149
{
130-
if (!typeInfo.IsEntity && !typeInfo.IsInterface)
131-
throw new ArgumentException(string.Format(Strings.ExPersistentTypeXIsNotEntityOrPersistentInterface, typeInfo.Name), "typeInfo");
132-
var fields = new List<PersistentFieldExpression>();
150+
if (!typeInfo.IsEntity && !typeInfo.IsInterface) {
151+
throw new ArgumentException(
152+
string.Format(Strings.ExPersistentTypeXIsNotEntityOrPersistentInterface, typeInfo.Name), nameof(typeInfo));
153+
}
154+
133155
var keyExpression = KeyExpression.Create(typeInfo, offset);
134-
fields.Add(keyExpression);
156+
157+
List<PersistentFieldExpression> fields;
135158
var result = new EntityExpression(typeInfo, keyExpression, null, false);
136159
if (keyFieldsOnly) {
160+
fields = new List<PersistentFieldExpression>(keyExpression.KeyFields.Count + 1) {keyExpression};
137161
// Add key fields to field collection
138-
var keyFieldClones = keyExpression
139-
.KeyFields
140-
.Select(kf=>FieldExpression.CreateField(kf.Field, offset))
141-
.Cast<PersistentFieldExpression>();
142-
fields.AddRange(keyFieldClones);
143-
}
144-
else
145-
foreach (var nestedField in typeInfo.Fields)
162+
foreach (var keyField in keyExpression.KeyFields) {
163+
// Do not convert to LINQ. We want to avoid a closure creation here.
164+
fields.Add(FieldExpression.CreateField(keyField.Field, offset));
165+
}
166+
}
167+
else {
168+
fields = new List<PersistentFieldExpression>(typeInfo.Fields.Count + 1) {keyExpression};
169+
foreach (var nestedField in typeInfo.Fields) {
170+
// Do not convert to LINQ. We want to avoid a closure creation here.
146171
fields.Add(BuildNestedFieldExpression(nestedField, offset));
172+
}
173+
}
174+
147175
result.Fields = fields;
148176
return result;
149177
}
150178

151179
public static EntityExpression Create(EntityFieldExpression entityFieldExpression, int offset)
152180
{
153181
var typeInfo = entityFieldExpression.PersistentType;
154-
var fields = new List<PersistentFieldExpression>();
155182
var keyExpression = KeyExpression.Create(typeInfo, offset);
156-
fields.Add(keyExpression);
157-
foreach (var nestedField in typeInfo.Fields)
158-
fields.Add(BuildNestedFieldExpression(nestedField, offset));
183+
var fields = new List<PersistentFieldExpression>(typeInfo.Fields.Count + 1) {keyExpression};
184+
foreach (var nestedField in typeInfo.Fields) {
185+
// Do not convert to LINQ. We want to avoid a closure creation here.
186+
fields.Add(BuildNestedFieldExpression(nestedField, offset));
187+
}
188+
159189
var result = new EntityExpression(typeInfo, keyExpression, null, entityFieldExpression.DefaultIfEmpty) {
160190
Fields = fields
161191
};
162-
if (entityFieldExpression.OuterParameter==null)
192+
if (entityFieldExpression.OuterParameter == null) {
163193
return result;
164-
return (EntityExpression) result.BindParameter(entityFieldExpression.OuterParameter, new Dictionary<Expression, Expression>());
194+
}
195+
196+
return (EntityExpression) result.BindParameter(
197+
entityFieldExpression.OuterParameter, new Dictionary<Expression, Expression>());
165198
}
166199

167200
private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo nestedField, int offset)
168201
{
169-
if (nestedField.IsPrimitive)
202+
if (nestedField.IsPrimitive) {
170203
return FieldExpression.CreateField(nestedField, offset);
171-
if (nestedField.IsStructure)
204+
}
205+
206+
if (nestedField.IsStructure) {
172207
return StructureFieldExpression.CreateStructure(nestedField, offset);
173-
if (nestedField.IsEntity)
208+
}
209+
210+
if (nestedField.IsEntity) {
174211
return EntityFieldExpression.CreateEntityField(nestedField, offset);
175-
if (nestedField.IsEntitySet)
176-
return EntitySetExpression.CreateEntitySet(nestedField);
177-
throw new NotSupportedException(string.Format(Strings.ExNestedFieldXIsNotSupported, nestedField.Attributes));
178-
}
212+
}
179213

180-
public override string ToString()
181-
{
182-
return string.Format("{0} {1}", base.ToString(), PersistentType.Name);
214+
if (nestedField.IsEntitySet) {
215+
return EntitySetExpression.CreateEntitySet(nestedField);
216+
}
217+
218+
throw new NotSupportedException(string.Format(Strings.ExNestedFieldXIsNotSupported, nestedField.Attributes));
183219
}
184220

221+
public override string ToString() => $"{base.ToString()} {PersistentType.Name}";
185222

186223
// Constructors
187224

0 commit comments

Comments
 (0)