Skip to content

Commit b2bd069

Browse files
authored
Optmize BuildFieldCheck: avoid double-calculation of field.ValueType.ToNullable() (#265)
* Optmize `BuildFieldCheck`: avoid double-calculation of `field.ValueType.ToNullable()` * Optimize ToConstantExpression() * Rename to StructDefaultConstantExpressions * Optimize BindParameters() * Optimize BindParameters() * Optimize BindParameters() * Simplify * Fix ToConstantExpression
1 parent 85070d6 commit b2bd069

File tree

3 files changed

+38
-57
lines changed

3 files changed

+38
-57
lines changed

Orm/Xtensive.Orm/Core/Extensions/ExpressionExtensions.cs

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace Xtensive.Core
2222
/// </summary>
2323
public static class ExpressionExtensions
2424
{
25-
private readonly static ConcurrentDictionary<Type, object> StructDefaultValues = new();
25+
private readonly static ConcurrentDictionary<Type, ConstantExpression> StructDefaultConstantExpressions = new();
2626

2727
/// <summary>
2828
/// Formats the <paramref name="expression"/>.
@@ -73,26 +73,31 @@ public static bool IsNull(this Expression expression)
7373
/// <exception cref="InvalidOperationException">Something went wrong :(.</exception>
7474
public static Expression BindParameters(this LambdaExpression lambdaExpression, params Expression[] parameters)
7575
{
76-
if (lambdaExpression.Parameters.Count!=parameters.Length)
76+
var lambdaExpressionParameters = lambdaExpression.Parameters;
77+
var lambdaExpressionParametersCount = lambdaExpressionParameters.Count;
78+
if (lambdaExpressionParametersCount != parameters.Length)
7779
throw new InvalidOperationException(String.Format(
7880
Strings.ExUnableToBindParametersToLambdaXParametersCountIsIncorrect,
7981
lambdaExpression.ToString(true)));
80-
if (parameters.Length==0)
82+
if (parameters.Length == 0)
8183
return lambdaExpression;
8284
var convertedParameters = new Expression[parameters.Length];
83-
for (int i = 0; i < lambdaExpression.Parameters.Count; i++) {
84-
var expressionParameter = lambdaExpression.Parameters[i];
85-
if (expressionParameter.Type.IsAssignableFrom(parameters[i].Type))
86-
convertedParameters[i] = expressionParameter.Type==parameters[i].Type
87-
? parameters[i]
88-
: Expression.Convert(parameters[i], expressionParameter.Type);
85+
for (int i = 0; i < lambdaExpressionParametersCount; i++) {
86+
var expressionParameter = lambdaExpressionParameters[i];
87+
var parameter = parameters[i];
88+
var parameterType = parameter.Type;
89+
var expressionParameterType = expressionParameter.Type;
90+
if (expressionParameterType.IsAssignableFrom(parameterType))
91+
convertedParameters[i] = expressionParameterType == parameterType
92+
? parameter
93+
: Expression.Convert(parameter, expressionParameterType);
8994
else
9095
throw new InvalidOperationException(String.Format(
9196
Strings.ExUnableToUseExpressionXAsXParameterOfLambdaXBecauseOfTypeMistmatch,
92-
parameters[i].ToString(true), i, lambdaExpression.Parameters[i].ToString(true)));
97+
parameters[i].ToString(true), i, expressionParameter.ToString(true)));
9398
}
9499
return ExpressionReplacer.ReplaceAll(
95-
lambdaExpression.Body, lambdaExpression.Parameters, convertedParameters);
100+
lambdaExpression.Body, lambdaExpressionParameters, convertedParameters);
96101
}
97102

98103
/// <summary>
@@ -121,28 +126,15 @@ public static Expression ToExpression(this SerializableExpression expression)
121126
/// </summary>
122127
/// <param name="defaultExpression">The expression to convert.</param>
123128
/// <returns>Result constant expression.</returns>
124-
public static ConstantExpression ToConstantExpression(this DefaultExpression defaultExpression)
125-
{
126-
var value = GetDefaultValue(defaultExpression);
127-
128-
return Expression.Constant(value, defaultExpression.Type);
129-
}
130-
131-
/// <summary>
132-
/// Gets the value represented by given <see cref="DefaultExpression"/>.
133-
/// </summary>
134-
/// <param name="defaultExpression">The default value expression.</param>
135-
/// <returns>Object value of default value.</returns>
136-
public static object GetDefaultValue(this DefaultExpression defaultExpression)
137-
{
138-
if (defaultExpression.Type.IsValueType) {
139-
return StructDefaultValues.GetOrAdd<DefaultExpression>(
140-
defaultExpression.Type,
141-
(type, expr) => { return ((Func<object>) Expression.Lambda(Expression.Convert(expr, WellKnownTypes.Object)).Compile()).Invoke(); },
142-
defaultExpression);
143-
}
144-
return null;
145-
}
129+
public static ConstantExpression ToConstantExpression(this DefaultExpression defaultExpression) =>
130+
StructDefaultConstantExpressions.GetOrAdd(
131+
defaultExpression.Type,
132+
static (t, expr) => Expression.Constant(
133+
t.IsValueType
134+
? ((Func<object>) Expression.Lambda(Expression.Convert(expr, WellKnownTypes.Object)).Compile()).Invoke()
135+
: null,
136+
t),
137+
defaultExpression);
146138

147139
/// <summary>
148140
/// Gets return type of <see cref="LambdaExpression"/>.
@@ -318,4 +310,4 @@ public static Expression StripMemberAccessChain(this Expression expression)
318310

319311
#endregion
320312
}
321-
}
313+
}

Orm/Xtensive.Orm/Linq/Internals/ExpressionHashCodeCalculator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ protected override int VisitConstant(ConstantExpression c)
6464
protected override int VisitDefault(DefaultExpression d)
6565
{
6666
if (d.Type.IsValueType) {
67-
return d.GetDefaultValue().GetHashCode();
67+
return d.ToConstantExpression().Value.GetHashCode();
6868
}
6969
else {
7070
return NullHashCode;
@@ -151,4 +151,4 @@ private int HashExpressionSequence(IEnumerable<Expression> expressions)
151151

152152
#endregion
153153
}
154-
}
154+
}

Orm/Xtensive.Orm/Orm/Building/Builders/PartialIndexFilterBuilder.cs

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,9 @@ protected override Expression VisitBinary(BinaryExpression b)
9494

9595
return base.VisitBinary(b);
9696

97-
static bool EnumRewritableOperations(BinaryExpression b)
98-
{
99-
var nt = b.NodeType;
100-
return nt == ExpressionType.Equal || nt == ExpressionType.NotEqual
101-
|| nt == ExpressionType.GreaterThan || nt == ExpressionType.GreaterThanOrEqual
102-
|| nt == ExpressionType.LessThan || nt == ExpressionType.LessThanOrEqual;
103-
}
97+
static bool EnumRewritableOperations(BinaryExpression b) =>
98+
b.NodeType is ExpressionType.Equal or ExpressionType.NotEqual or ExpressionType.GreaterThan or ExpressionType.GreaterThanOrEqual
99+
or ExpressionType.LessThan or ExpressionType.LessThanOrEqual;
104100
}
105101

106102
protected override Expression VisitMember(MemberExpression originalMemberAccess)
@@ -142,7 +138,7 @@ protected override Expression VisitMember(MemberExpression originalMemberAccess)
142138
}
143139
if (field.IsPrimitive) {
144140
EnsureCanBeUsedInFilter(originalMemberAccess, field);
145-
return BuildFieldAccess(field, false);
141+
return BuildFieldAccess(field, field.ValueType);
146142
}
147143
throw UnableToTranslate(originalMemberAccess, Strings.OnlyPrimitiveAndReferenceFieldsAreSupported);
148144
}
@@ -156,10 +152,9 @@ private void EnsureCanBeUsedInFilter(Expression expression, FieldInfo field)
156152
throw UnableToTranslate(expression, string.Format(Strings.FieldXDoesNotExistInTableForY, field.Name, field.ReflectedType));
157153
}
158154

159-
private Expression BuildFieldAccess(FieldInfo field, bool addNullability)
155+
private Expression BuildFieldAccess(FieldInfo field, Type valueType)
160156
{
161157
var fieldIndex = usedFields.Count;
162-
var valueType = addNullability ? field.ValueType.ToNullable() : field.ValueType;
163158
usedFields.Add(field);
164159
return Expression.Call(Parameter,
165160
WellKnownMembers.Tuple.GenericAccessor.CachedMakeGenericMethod(valueType),
@@ -168,18 +163,12 @@ private Expression BuildFieldAccess(FieldInfo field, bool addNullability)
168163

169164
private Expression BuildFieldCheck(FieldInfo field, ExpressionType nodeType)
170165
{
171-
return Expression.MakeBinary(nodeType, BuildFieldAccess(field, true), Expression.Constant(null, field.ValueType.ToNullable()));
166+
var nullableValueType = field.ValueType.ToNullable();
167+
return Expression.MakeBinary(nodeType, BuildFieldAccess(field, nullableValueType), Expression.Constant(null, nullableValueType));
172168
}
173169

174-
private Expression BuildEntityCheck(FieldInfo field, ExpressionType nodeType)
175-
{
176-
var fields = field.Fields.Where(f => f.Column!=null).ToList();
177-
if (fields.Count==0)
178-
throw new InvalidOperationException();
179-
return fields
180-
.Skip(1)
181-
.Aggregate(BuildFieldCheck(fields[0], nodeType), (c, f) => Expression.AndAlso(c, BuildFieldCheck(f, nodeType)));
182-
}
170+
private Expression BuildEntityCheck(FieldInfo field, ExpressionType nodeType) =>
171+
field.Fields.Where(f => f.Column != null).Select(f => BuildFieldCheck(f, nodeType)).Aggregate(Expression.AndAlso);
183172

184173
private bool IsNull(Expression expression)
185174
{
@@ -231,4 +220,4 @@ private PartialIndexFilterBuilder(IndexInfo index)
231220
reflectedType = index.ReflectedType;
232221
}
233222
}
234-
}
223+
}

0 commit comments

Comments
 (0)