Skip to content

Commit 438200c

Browse files
committed
Get rid of GetGenericMethodDefinition method calls because it may lead to thread contention and to lock convoy issue (#16)
1 parent 2bfd2a3 commit 438200c

File tree

4 files changed

+100
-69
lines changed

4 files changed

+100
-69
lines changed

Orm/Xtensive.Orm/Orm/Linq/Model/QueryParser.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,37 @@
77
using System;
88
using System.Linq.Expressions;
99
using Xtensive.Core;
10+
using Xtensive.Reflection;
1011

1112
namespace Xtensive.Orm.Linq.Model
1213
{
1314
internal static class QueryParser
1415
{
1516
public static GroupByQuery ParseGroupBy(MethodCallExpression mc)
1617
{
17-
var method = mc.Method.GetGenericMethodDefinition();
18+
var method = mc.Method;
1819

19-
if (method==QueryableMethodInfo.GroupBy)
20+
if (method.IsGenericMethodSpecificationOf(QueryableMethodInfo.GroupBy))
2021
return new GroupByQuery {
2122
Source = mc.Arguments[0],
2223
KeySelector = mc.Arguments[1].StripQuotes(),
2324
};
2425

25-
if (method==QueryableMethodInfo.GroupByWithElementSelector)
26+
if (method.IsGenericMethodSpecificationOf(QueryableMethodInfo.GroupByWithElementSelector))
2627
return new GroupByQuery {
2728
Source = mc.Arguments[0],
2829
KeySelector = mc.Arguments[1].StripQuotes(),
2930
ElementSelector = mc.Arguments[2].StripQuotes(),
3031
};
3132

32-
if (method==QueryableMethodInfo.GroupByWithResultSelector)
33+
if (method.IsGenericMethodSpecificationOf(QueryableMethodInfo.GroupByWithResultSelector))
3334
return new GroupByQuery {
3435
Source = mc.Arguments[0],
3536
KeySelector = mc.Arguments[1].StripQuotes(),
3637
ResultSelector = mc.Arguments[2].StripQuotes(),
3738
};
3839

39-
if (method==QueryableMethodInfo.GroupByWithElementAndResultSelectors)
40+
if (method.IsGenericMethodSpecificationOf(QueryableMethodInfo.GroupByWithElementAndResultSelectors))
4041
return new GroupByQuery {
4142
Source = mc.Arguments[0],
4243
KeySelector = mc.Arguments[1].StripQuotes(),

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

Lines changed: 73 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -327,103 +327,117 @@ protected override Expression VisitMethodCall(MethodCallExpression mc)
327327
{
328328
using (state.CreateScope()) {
329329
state.IsTailMethod = mc==context.Query && mc.IsQuery();
330-
var customCompiler = context.CustomCompilerProvider.GetCompiler(mc.Method);
331-
if (customCompiler!=null)
330+
var method = mc.Method;
331+
var customCompiler = context.CustomCompilerProvider.GetCompiler(method);
332+
if (customCompiler!=null) {
332333
return Visit(customCompiler.Invoke(mc.Object, mc.Arguments.ToArray()));
334+
}
333335

334336
// Visit Query. Deprecated.
335337
#pragma warning disable 612,618
336-
if (mc.Method.DeclaringType==typeof (Query)) {
338+
if (method.DeclaringType==typeof (Query)) {
337339
// Query.All<T>
338-
if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition()==WellKnownMembers.Query.All)
340+
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.All)) {
339341
return ConstructQueryable(mc);
342+
}
343+
340344
// Query.FreeText<T>
341-
if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition()
342-
.In(WellKnownMembers.Query.FreeTextString,
343-
WellKnownMembers.Query.FreeTextExpression,
344-
WellKnownMembers.Query.FreeTextExpressionTopNByRank,
345-
WellKnownMembers.Query.FreeTextStringTopNByRank))
346-
return ConstructFreeTextQueryRoot(mc.Method.GetGenericArguments()[0], mc.Arguments);
345+
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.FreeTextString)
346+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.FreeTextExpression)
347+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.FreeTextExpressionTopNByRank)
348+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.FreeTextStringTopNByRank)) {
349+
return ConstructFreeTextQueryRoot(method.GetGenericArguments()[0], mc.Arguments);
350+
}
351+
347352
// Query.ContainsTable<T>
348-
if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition()
349-
.In(WellKnownMembers.Query.ContainsTableExpr,
350-
WellKnownMembers.Query.ContainsTableExprWithColumns,
351-
WellKnownMembers.Query.ContainsTableExprTopNByRank,
352-
WellKnownMembers.Query.ContainsTableExprWithColumnsTopNByRank))
353-
return ConstructContainsTableQueryRoot(mc.Method.GetGenericArguments()[0], mc.Arguments);
353+
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.ContainsTableExpr)
354+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.ContainsTableExprWithColumns)
355+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.ContainsTableExprTopNByRank)
356+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.ContainsTableExprWithColumnsTopNByRank)) {
357+
return ConstructContainsTableQueryRoot(method.GetGenericArguments()[0], mc.Arguments);
358+
}
359+
354360
// Query.Single<T> & Query.SingleOrDefault<T>
355-
if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition().In(
356-
WellKnownMembers.Query.SingleKey,
357-
WellKnownMembers.Query.SingleOrDefaultKey))
361+
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.SingleKey)
362+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.SingleOrDefaultKey)) {
358363
return VisitQuerySingle(mc);
364+
}
365+
359366
throw new InvalidOperationException(String.Format(Strings.ExMethodCallExpressionXIsNotSupported, mc.ToString(true)));
360367
}
361368
// Visit QueryEndpoint.
362-
if (mc.Method.DeclaringType == typeof(QueryEndpoint)) {
369+
if (method.DeclaringType == typeof(QueryEndpoint)) {
363370
// Query.All<T>
364-
if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition() == WellKnownMembers.QueryEndpoint.All)
371+
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.All)) {
365372
return ConstructQueryable(mc);
373+
}
374+
366375
// Query.FreeText<T>
367-
if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition()
368-
.In(WellKnownMembers.QueryEndpoint.FreeTextString,
369-
WellKnownMembers.QueryEndpoint.FreeTextExpression,
370-
WellKnownMembers.Query.FreeTextExpressionTopNByRank,
371-
WellKnownMembers.Query.FreeTextStringTopNByRank))
372-
return ConstructFreeTextQueryRoot(mc.Method.GetGenericArguments()[0], mc.Arguments);
376+
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.FreeTextString)
377+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.FreeTextExpression)
378+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.FreeTextExpressionTopNByRank)
379+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.FreeTextStringTopNByRank)) {
380+
return ConstructFreeTextQueryRoot(method.GetGenericArguments()[0], mc.Arguments);
381+
}
382+
373383
// Query.ContainsTable<T>
374-
if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition()
375-
.In(WellKnownMembers.QueryEndpoint.ContainsTableExpr,
376-
WellKnownMembers.QueryEndpoint.ContainsTableExprWithColumns,
377-
WellKnownMembers.QueryEndpoint.ContainsTableExprTopNByRank,
378-
WellKnownMembers.QueryEndpoint.ContainsTableExprWithColumnsTopNByRank))
379-
return ConstructContainsTableQueryRoot(mc.Method.GetGenericArguments()[0], mc.Arguments);
384+
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.ContainsTableExpr)
385+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.ContainsTableExprWithColumns)
386+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.ContainsTableExprTopNByRank)
387+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.ContainsTableExprWithColumnsTopNByRank)) {
388+
return ConstructContainsTableQueryRoot(method.GetGenericArguments()[0], mc.Arguments);
389+
}
390+
380391
// Query.Single<T> & Query.SingleOrDefault<T>
381-
if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition().In(
382-
WellKnownMembers.QueryEndpoint.SingleKey,
383-
WellKnownMembers.QueryEndpoint.SingleOrDefaultKey))
392+
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.SingleKey)
393+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.SingleOrDefaultKey)) {
384394
return VisitQuerySingle(mc);
385-
if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition()==WellKnownMembers.QueryEndpoint.Items)
395+
}
396+
397+
if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.Items)) {
386398
return VisitSequence(mc.Arguments[0].StripQuotes().Body, mc);
399+
}
400+
387401
throw new InvalidOperationException(String.Format(Strings.ExMethodCallExpressionXIsNotSupported, mc.ToString(true)));
388402
}
389403
#pragma warning restore 612,618
390404

391405
// Visit Queryable extensions.
392-
if (mc.Method.DeclaringType==typeof (QueryableExtensions))
393-
if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionLeftJoin.Name)
406+
if (method.DeclaringType==typeof (QueryableExtensions))
407+
if (method.Name==WellKnownMembers.Queryable.ExtensionLeftJoin.Name)
394408
return VisitLeftJoin(mc);
395-
else if (mc.Method.Name=="In")
409+
else if (method.Name=="In")
396410
return VisitIn(mc);
397-
else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionLock.Name)
411+
else if (method.Name==WellKnownMembers.Queryable.ExtensionLock.Name)
398412
return VisitLock(mc);
399-
else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionTake.Name)
413+
else if (method.Name==WellKnownMembers.Queryable.ExtensionTake.Name)
400414
return VisitTake(mc.Arguments[0], mc.Arguments[1]);
401-
else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionSkip.Name)
415+
else if (method.Name==WellKnownMembers.Queryable.ExtensionSkip.Name)
402416
return VisitSkip(mc.Arguments[0], mc.Arguments[1]);
403-
else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionElementAt.Name)
404-
return VisitElementAt(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.ReturnType, false);
405-
else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionElementAtOrDefault.Name)
406-
return VisitElementAt(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.ReturnType, true);
407-
else if (mc.Method.Name == WellKnownMembers.Queryable.ExtensionCount.Name)
408-
return VisitAggregate(mc.Arguments[0], mc.Method, null, context.IsRoot(mc), mc);
417+
else if (method.Name==WellKnownMembers.Queryable.ExtensionElementAt.Name)
418+
return VisitElementAt(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.ReturnType, false);
419+
else if (method.Name==WellKnownMembers.Queryable.ExtensionElementAtOrDefault.Name)
420+
return VisitElementAt(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.ReturnType, true);
421+
else if (method.Name == WellKnownMembers.Queryable.ExtensionCount.Name)
422+
return VisitAggregate(mc.Arguments[0], method, null, context.IsRoot(mc), mc);
409423
else
410424
throw new InvalidOperationException(String.Format(Strings.ExMethodCallExpressionXIsNotSupported, mc.ToString(true)));
411425
// Visit Collection extensions
412-
if (mc.Method.DeclaringType==typeof (CollectionExtensions))
413-
if (mc.Method.Name==WellKnownMembers.Collection.ExtensionContainsAny.Name)
414-
return VisitContainsAny(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.GetGenericArguments()[0]);
415-
else if (mc.Method.Name==WellKnownMembers.Collection.ExtensionContainsAll.Name)
416-
return VisitContainsAll(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.GetGenericArguments()[0]);
417-
else if (mc.Method.Name==WellKnownMembers.Collection.ExtensionContainsNone.Name)
418-
return VisitContainsNone(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.GetGenericArguments()[0]);
426+
if (method.DeclaringType==typeof (CollectionExtensions))
427+
if (method.Name==WellKnownMembers.Collection.ExtensionContainsAny.Name)
428+
return VisitContainsAny(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.GetGenericArguments()[0]);
429+
else if (method.Name==WellKnownMembers.Collection.ExtensionContainsAll.Name)
430+
return VisitContainsAll(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.GetGenericArguments()[0]);
431+
else if (method.Name==WellKnownMembers.Collection.ExtensionContainsNone.Name)
432+
return VisitContainsNone(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.GetGenericArguments()[0]);
419433

420434
// Process local collections
421435
if (mc.Object.IsLocalCollection(context)) {
422436
// IList.Contains
423-
// List.Contains
437+
// List.Contains
424438
// Array.Contains
425-
ParameterInfo[] parameters = mc.Method.GetParameters();
426-
if (mc.Method.Name=="Contains" && parameters.Length==1)
439+
ParameterInfo[] parameters = method.GetParameters();
440+
if (method.Name=="Contains" && parameters.Length==1)
427441
return VisitContains(mc.Object, mc.Arguments[0], false);
428442
}
429443

Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,12 +1086,12 @@ private ProjectionExpression VisitSelectMany(Expression source, LambdaExpression
10861086
bool isOuter = false;
10871087
if (collectionSelector.Body.NodeType==ExpressionType.Call) {
10881088
var call = (MethodCallExpression) collectionSelector.Body;
1089-
var genericMethodDefinition = call.Method.GetGenericMethodDefinition();
1090-
isOuter = call.Method.IsGenericMethod
1091-
&& (genericMethodDefinition==WellKnownMembers.Queryable.DefaultIfEmpty
1092-
|| genericMethodDefinition==WellKnownMembers.Enumerable.DefaultIfEmpty);
1093-
if (isOuter)
1089+
var method = call.Method;
1090+
isOuter = method.IsGenericMethodSpecificationOf(WellKnownMembers.Queryable.DefaultIfEmpty)
1091+
|| method.IsGenericMethodSpecificationOf(WellKnownMembers.Enumerable.DefaultIfEmpty);
1092+
if (isOuter) {
10941093
collectionSelector = FastExpression.Lambda(call.Arguments[0], outerParameter);
1094+
}
10951095
}
10961096
ProjectionExpression innerProjection;
10971097
using (state.CreateScope()) {

Orm/Xtensive.Orm/Reflection/TypeHelper.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,22 @@ public static bool IsNullable(this Type type) =>
891891
/// <returns><see cref="MethodInfo"/> object describing the delegate "Invoke" method.</returns>
892892
public static MethodInfo GetInvokeMethod(this Type delegateType) => delegateType.GetMethod(invokeMethodName);
893893

894+
895+
/// <summary>
896+
/// Determines whether given <paramref name="method"/> is a specification
897+
/// of the provided <paramref name="genericMethodDefinition"/>.
898+
/// </summary>
899+
/// <param name="method">The <see cref="MethodInfo"/> to check.</param>
900+
/// <param name="genericMethodDefinition">The <see cref="MethodInfo"/> of the generic method definition
901+
/// to check against.</param>
902+
/// <returns><see langword="true"/> if the specified <paramref name="method"/> is a specification
903+
/// of the provided <paramref name="genericMethodDefinition"/>.</returns>
904+
public static bool IsGenericMethodSpecificationOf(this MethodInfo method, MethodInfo genericMethodDefinition) =>
905+
method.MetadataToken == genericMethodDefinition.MetadataToken
906+
&& (ReferenceEquals(method.Module, genericMethodDefinition.Module)
907+
|| method.Module == genericMethodDefinition.Module)
908+
&& method.IsGenericMethod && genericMethodDefinition.IsGenericMethodDefinition;
909+
894910
/// <summary>
895911
/// Determines whether the specified <paramref name="type"/> is an ancestor or an instance of the
896912
/// provided <paramref name="openGenericBaseType"/>.

0 commit comments

Comments
 (0)