Skip to content

Commit ae0f1f7

Browse files
authored
Optimize LINQ batch item processing for queries with overridden result type (#2350)
1 parent 8b5bd0f commit ae0f1f7

File tree

3 files changed

+43
-28
lines changed

3 files changed

+43
-28
lines changed

src/NHibernate/Async/Multi/LinqBatchItem.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@
1414
using System.Linq;
1515
using System.Linq.Expressions;
1616
using NHibernate.Linq;
17-
using NHibernate.Util;
1817
using Remotion.Linq.Parsing.ExpressionVisitors;
1918

2019
namespace NHibernate.Multi
2120
{
2221
using System.Threading.Tasks;
2322
using System.Threading;
2423

25-
public partial class LinqBatchItem<T> : QueryBatchItem<T>
24+
public partial class LinqBatchItem<T> : QueryBatchItem<T>, ILinqBatchItem
2625
{
2726

2827
protected override async Task<IList<T>> GetResultsNonBatchedAsync(CancellationToken cancellationToken)

src/NHibernate/Multi/LinqBatchItem.cs

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
using System.Linq;
55
using System.Linq.Expressions;
66
using NHibernate.Linq;
7-
using NHibernate.Util;
87
using Remotion.Linq.Parsing.ExpressionVisitors;
98

109
namespace NHibernate.Multi
1110
{
11+
interface ILinqBatchItem
12+
{
13+
List<TResult> GetTypedResults<TResult>();
14+
}
15+
1216
public static class LinqBatchItem
1317
{
1418
public static LinqBatchItem<TResult> Create<T, TResult>(IQueryable<T> query, Expression<Func<IQueryable<T>, TResult>> selector)
@@ -42,9 +46,10 @@ private static LinqBatchItem<TResult> GetForQuery<TResult>(IQueryable query, Exp
4246
/// Create instance via <see cref="LinqBatchItem.Create"/> methods
4347
/// </summary>
4448
/// <typeparam name="T">Result type</typeparam>
45-
public partial class LinqBatchItem<T> : QueryBatchItem<T>
49+
public partial class LinqBatchItem<T> : QueryBatchItem<T>, ILinqBatchItem
4650
{
4751
private readonly Delegate _postExecuteTransformer;
52+
private readonly System.Type _resultTypeOverride;
4853

4954
public LinqBatchItem(IQuery query) : base(query)
5055
{
@@ -53,6 +58,7 @@ public LinqBatchItem(IQuery query) : base(query)
5358
internal LinqBatchItem(IQuery query, NhLinqExpression linq) : base(query)
5459
{
5560
_postExecuteTransformer = linq.ExpressionToHqlTranslationResults.PostExecuteTransformer;
61+
_resultTypeOverride = linq.ExpressionToHqlTranslationResults.ExecuteResultTypeOverride;
5662
}
5763

5864
protected override IList<T> GetResultsNonBatched()
@@ -69,11 +75,10 @@ protected override List<T> DoGetResults()
6975
{
7076
if (_postExecuteTransformer != null)
7177
{
72-
var elementType = GetResultTypeIfChanged();
73-
74-
IList transformerList = elementType == null
78+
IList transformerList = _resultTypeOverride == null
7579
? base.DoGetResults()
76-
: GetTypedResults(elementType);
80+
//see LinqToFutureValueFixture tests that cover this scenario
81+
: LinqBatchReflectHelper.GetTypedResults(this, _resultTypeOverride);
7782

7883
return GetTransformedResults(transformerList);
7984
}
@@ -90,27 +95,9 @@ private List<T> GetTransformedResults(IList transformerList)
9095
};
9196
}
9297

93-
private System.Type GetResultTypeIfChanged()
94-
{
95-
if (_postExecuteTransformer == null)
96-
{
97-
return null;
98-
}
99-
var elementType = _postExecuteTransformer.Method.GetParameters()[1].ParameterType.GetGenericArguments()[0];
100-
if (typeof(T).IsAssignableFrom(elementType))
101-
{
102-
return null;
103-
}
104-
105-
return elementType;
106-
}
107-
108-
private IList GetTypedResults(System.Type type)
98+
List<TResult> ILinqBatchItem.GetTypedResults<TResult>()
10999
{
110-
var method = ReflectHelper.GetMethod(() => GetTypedResults<T>())
111-
.GetGenericMethodDefinition();
112-
var generic = method.MakeGenericMethod(type);
113-
return (IList) generic.Invoke(this, null);
100+
return GetTypedResults<TResult>();
114101
}
115102
}
116103
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Concurrent;
4+
using System.Linq.Expressions;
5+
using System.Reflection;
6+
using NHibernate.Util;
7+
8+
namespace NHibernate.Multi
9+
{
10+
static class LinqBatchReflectHelper
11+
{
12+
private static readonly ConcurrentDictionary<System.Type, Func<ILinqBatchItem, IList>> GetResultsForTypeDic = new ConcurrentDictionary<System.Type, Func<ILinqBatchItem, IList>>();
13+
14+
private static readonly MethodInfo GetTypedResultsMethod = ReflectHelper.GetMethod((ILinqBatchItem i) => i.GetTypedResults<object>()).GetGenericMethodDefinition();
15+
16+
internal static IList GetTypedResults(ILinqBatchItem batchItem, System.Type type)
17+
{
18+
return GetResultsForTypeDic.GetOrAdd(type, t => CompileDelegate(t)).Invoke(batchItem);
19+
}
20+
21+
private static Func<ILinqBatchItem, IList> CompileDelegate(System.Type type)
22+
{
23+
var generic = GetTypedResultsMethod.MakeGenericMethod(type);
24+
var instance = Expression.Parameter(typeof(ILinqBatchItem));
25+
var methodCall = Expression.Call(instance, generic);
26+
return Expression.Lambda<Func<ILinqBatchItem, IList>>(methodCall, instance).Compile();
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)