Skip to content

Commit adf1439

Browse files
authored
Replace slow Enum.TryParse() by dictionary lookup (#276)
* Replace slow `Enum.TryParse()` by dictionary lookup * Simplify GetQueryableMethod() * reformat * refactor * Return null on unkonwn method
1 parent a669a14 commit adf1439

File tree

2 files changed

+156
-138
lines changed

2 files changed

+156
-138
lines changed

Orm/Xtensive.Orm/Linq/QueryableVisitor.cs

Lines changed: 82 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,69 +4,92 @@
44
// Created by: Alexis Kochetov
55
// Created: 2009.02.25
66

7-
using System;
8-
using System.Collections.Generic;
9-
using System.Linq;
107
using System.Linq.Expressions;
118
using Xtensive.Reflection;
129

13-
namespace Xtensive.Linq
14-
{
15-
/// <summary>
16-
/// Abstract base visitor that handles methods of <see cref="IQueryable"/> and <see cref="IEnumerable{T}"/> by calling <see cref="VisitQueryableMethod"/>.
17-
/// </summary>
18-
[Serializable]
19-
public abstract class QueryableVisitor : ExpressionVisitor
20-
{
21-
/// <inheritdoc/>
22-
protected override Expression VisitMethodCall(MethodCallExpression mc)
23-
{
24-
if (mc.Arguments.Count > 0 && mc.Arguments[0].Type == WellKnownTypes.String) {
25-
return base.VisitMethodCall(mc);
26-
}
27-
28-
var method = GetQueryableMethod(mc);
29-
if (method == null) {
30-
return base.VisitMethodCall(mc);
31-
}
32-
33-
return VisitQueryableMethod(mc, method.Value);
34-
}
35-
36-
/// <summary>
37-
/// Visits method of <see cref="IQueryable"/> or <see cref="IEnumerable{T}"/>.
38-
/// </summary>
39-
/// <param name="mc">The method call expression.</param>
40-
/// <param name="methodKind">Kind of the method.</param>
41-
protected abstract Expression VisitQueryableMethod(MethodCallExpression mc, QueryableMethodKind methodKind);
10+
namespace Xtensive.Linq;
4211

43-
/// <summary>
44-
/// Parses <see cref="QueryableMethodKind"/> for the specified expression.
45-
/// </summary>
46-
/// <param name="call">A call to process.</param>
47-
/// <returns><see cref="QueryableMethodKind"/> for the specified expression,
48-
/// or null if method is not a LINQ method.</returns>
49-
public static QueryableMethodKind? GetQueryableMethod(MethodCallExpression call)
50-
{
51-
if (call == null) {
52-
return null;
53-
}
54-
55-
var declaringType = call.Method.DeclaringType;
56-
if (declaringType == WellKnownTypes.Queryable || declaringType == WellKnownTypes.Enumerable) {
57-
return ParseQueryableMethodKind(call.Method.Name);
58-
}
12+
/// <summary>
13+
/// Abstract base visitor that handles methods of <see cref="IQueryable"/> and <see cref="IEnumerable{T}"/> by calling <see cref="VisitQueryableMethod"/>.
14+
/// </summary>
15+
[Serializable]
16+
public abstract class QueryableVisitor : ExpressionVisitor
17+
{
18+
private static readonly Dictionary<string, QueryableMethodKind> QueryableMethodKindFromName = new() {
19+
[nameof(Queryable.Aggregate)] = QueryableMethodKind.Aggregate,
20+
[nameof(Queryable.All)] = QueryableMethodKind.All,
21+
[nameof(Queryable.Any)] = QueryableMethodKind.Any,
22+
["AsEnumerable"] = QueryableMethodKind.AsEnumerable,
23+
["AsQueryable"] = QueryableMethodKind.AsQueryable,
24+
[nameof(Queryable.Average)] = QueryableMethodKind.Average,
25+
[nameof(Queryable.Cast)] = QueryableMethodKind.Cast,
26+
[nameof(Queryable.Concat)] = QueryableMethodKind.Concat,
27+
[nameof(Queryable.Contains)] = QueryableMethodKind.Contains,
28+
[nameof(Queryable.Count)] = QueryableMethodKind.Count,
29+
[nameof(Queryable.DefaultIfEmpty)] = QueryableMethodKind.DefaultIfEmpty,
30+
[nameof(Queryable.Distinct)] = QueryableMethodKind.Distinct,
31+
[nameof(Queryable.DistinctBy)] = QueryableMethodKind.DistinctBy,
32+
[nameof(Queryable.ElementAt)] = QueryableMethodKind.ElementAt,
33+
[nameof(Queryable.ElementAtOrDefault)] = QueryableMethodKind.ElementAtOrDefault,
34+
[nameof(Queryable.Except)] = QueryableMethodKind.Except,
35+
[nameof(Queryable.First)] = QueryableMethodKind.First,
36+
[nameof(Queryable.FirstOrDefault)] = QueryableMethodKind.FirstOrDefault,
37+
[nameof(Queryable.GroupBy)] = QueryableMethodKind.GroupBy,
38+
[nameof(Queryable.GroupJoin)] = QueryableMethodKind.GroupJoin,
39+
[nameof(Queryable.Intersect)] = QueryableMethodKind.Intersect,
40+
[nameof(Queryable.Join)] = QueryableMethodKind.Join,
41+
[nameof(Queryable.Last)] = QueryableMethodKind.Last,
42+
[nameof(Queryable.LastOrDefault)] = QueryableMethodKind.LastOrDefault,
43+
[nameof(Queryable.LongCount)] = QueryableMethodKind.LongCount,
44+
[nameof(Queryable.Max)] = QueryableMethodKind.Max,
45+
[nameof(Queryable.Min)] = QueryableMethodKind.Min,
46+
[nameof(Queryable.OfType)] = QueryableMethodKind.OfType,
47+
[nameof(Queryable.OrderBy)] = QueryableMethodKind.OrderBy,
48+
[nameof(Queryable.OrderByDescending)] = QueryableMethodKind.OrderByDescending,
49+
[nameof(Queryable.Reverse)] = QueryableMethodKind.Reverse,
50+
[nameof(Queryable.Select)] = QueryableMethodKind.Select,
51+
[nameof(Queryable.SelectMany)] = QueryableMethodKind.SelectMany,
52+
[nameof(Queryable.SequenceEqual)] = QueryableMethodKind.SequenceEqual,
53+
[nameof(Queryable.Single)] = QueryableMethodKind.Single,
54+
[nameof(Queryable.SingleOrDefault)] = QueryableMethodKind.SingleOrDefault,
55+
[nameof(Queryable.Skip)] = QueryableMethodKind.Skip,
56+
[nameof(Queryable.SkipWhile)] = QueryableMethodKind.SkipWhile,
57+
[nameof(Queryable.Sum)] = QueryableMethodKind.Sum,
58+
[nameof(Queryable.Take)] = QueryableMethodKind.Take,
59+
[nameof(Queryable.TakeWhile)] = QueryableMethodKind.TakeWhile,
60+
[nameof(Queryable.ThenBy)] = QueryableMethodKind.ThenBy,
61+
[nameof(Queryable.ThenByDescending)] = QueryableMethodKind.ThenByDescending,
62+
["ToArray"] = QueryableMethodKind.ToArray,
63+
["ToList"] = QueryableMethodKind.ToList,
64+
[nameof(Queryable.Union)] = QueryableMethodKind.Union,
65+
[nameof(Queryable.Where)] = QueryableMethodKind.Where
66+
};
5967

60-
return null;
61-
}
68+
/// <inheritdoc/>
69+
protected override Expression VisitMethodCall(MethodCallExpression mc) =>
70+
mc.Arguments is var mcArguments
71+
&& (mcArguments.Count > 0 && mcArguments[0].Type == WellKnownTypes.String)
72+
|| !(GetQueryableMethod(mc) is { } method)
73+
? base.VisitMethodCall(mc)
74+
: VisitQueryableMethod(mc, method);
6275

63-
private static QueryableMethodKind? ParseQueryableMethodKind(string methodName)
64-
{
65-
if (Enum.TryParse(methodName, out QueryableMethodKind result)) {
66-
return result;
67-
}
76+
/// <summary>
77+
/// Visits method of <see cref="IQueryable"/> or <see cref="IEnumerable{T}"/>.
78+
/// </summary>
79+
/// <param name="mc">The method call expression.</param>
80+
/// <param name="methodKind">Kind of the method.</param>
81+
protected abstract Expression VisitQueryableMethod(MethodCallExpression mc, QueryableMethodKind methodKind);
6882

69-
return null;
70-
}
71-
}
72-
}
83+
/// <summary>
84+
/// Parses <see cref="QueryableMethodKind"/> for the specified expression.
85+
/// </summary>
86+
/// <param name="call">A call to process.</param>
87+
/// <returns><see cref="QueryableMethodKind"/> for the specified expression,
88+
/// or null if method is not a LINQ method.</returns>
89+
public static QueryableMethodKind? GetQueryableMethod(MethodCallExpression call) =>
90+
call?.Method.DeclaringType is { } declaringType
91+
&& (declaringType == WellKnownTypes.Queryable || declaringType == WellKnownTypes.Enumerable)
92+
&& QueryableMethodKindFromName.TryGetValue(call.Method.Name, out var v)
93+
? v
94+
: null;
95+
}

0 commit comments

Comments
 (0)