Skip to content

Commit 7270474

Browse files
authored
Added Helper to convert a ISelection to an Expression. (#7531)
1 parent 3eac31b commit 7270474

File tree

2 files changed

+79
-55
lines changed

2 files changed

+79
-55
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System.Buffers.Text;
2+
using System.Linq.Expressions;
3+
using System.Text;
4+
using System.Runtime.CompilerServices;
5+
using HotChocolate.Execution.Projections;
6+
7+
// ReSharper disable once CheckNamespace
8+
namespace HotChocolate.Execution.Processing;
9+
10+
/// <summary>
11+
/// Provides extension methods to work with selections.
12+
/// </summary>
13+
public static class HotChocolateExecutionSelectionExtensions
14+
{
15+
private static readonly SelectionExpressionBuilder _builder = new();
16+
17+
/// <summary>
18+
/// Creates a selector expression from a GraphQL selection.
19+
/// </summary>
20+
/// <param name="selection">
21+
/// The selection that shall be converted into a selector expression.
22+
/// </param>
23+
/// <typeparam name="TValue">
24+
/// The type of the value that is returned by the <see cref="ISelection"/>.
25+
/// </typeparam>
26+
/// <returns>
27+
/// Returns a selector expression that can be used for data projections.
28+
/// </returns>
29+
public static Expression<Func<TValue, TValue>> ToSelectorExpression<TValue>(
30+
this ISelection selection)
31+
=> GetOrCreateExpression<TValue>(selection);
32+
33+
private static Expression<Func<TValue, TValue>> GetOrCreateExpression<TValue>(
34+
ISelection selection)
35+
{
36+
return selection.DeclaringOperation.GetOrAddState(
37+
CreateExpressionKey(selection.Id),
38+
static (_, ctx) => ctx._builder.BuildExpression<TValue>(ctx.selection),
39+
(_builder, selection));
40+
}
41+
42+
private static string CreateExpressionKey(int key)
43+
{
44+
var keyPrefix = GetKeyPrefix();
45+
var requiredBufferSize = EstimateIntLength(key) + keyPrefix.Length;
46+
Span<byte> span = stackalloc byte[requiredBufferSize];
47+
keyPrefix.CopyTo(span);
48+
Utf8Formatter.TryFormat(key, span.Slice(keyPrefix.Length), out var written, 'D');
49+
return Encoding.UTF8.GetString(span.Slice(0, written + keyPrefix.Length));
50+
}
51+
52+
private static ReadOnlySpan<byte> GetKeyPrefix()
53+
=> "hc-dataloader-expr-"u8;
54+
55+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
56+
private static int EstimateIntLength(int value)
57+
{
58+
if (value == 0)
59+
{
60+
// to print 0 we need still 1 digit
61+
return 1;
62+
}
63+
64+
// if the number is negative we need one more digit for the sign
65+
var length = (value < 0) ? 1 : 0;
66+
67+
// we add the number of digits the number has to the length of the number.
68+
length += (int)Math.Floor(Math.Log10(Math.Abs(value)) + 1);
69+
70+
return length;
71+
}
72+
}

src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs

+7-55
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
using System.Buffers;
2-
using System.Buffers.Text;
32
using System.Diagnostics.CodeAnalysis;
4-
using System.Linq.Expressions;
5-
using System.Runtime.CompilerServices;
6-
using System.Text;
73
using HotChocolate.Execution;
84
using HotChocolate.Execution.Processing;
9-
using HotChocolate.Execution.Projections;
105
using HotChocolate.Pagination;
116
using HotChocolate.Types;
127
using HotChocolate.Types.Descriptors.Definitions;
@@ -21,8 +16,6 @@ namespace GreenDonut.Projections;
2116
[Experimental(Experiments.Projections)]
2217
public static class HotChocolateExecutionDataLoaderExtensions
2318
{
24-
private static readonly SelectionExpressionBuilder _builder = new();
25-
2619
/// <summary>
2720
/// Selects the fields that where selected in the GraphQL selection tree.
2821
/// </summary>
@@ -46,7 +39,7 @@ public static ISelectionDataLoader<TKey, TValue> Select<TKey, TValue>(
4639
ISelection selection)
4740
where TKey : notnull
4841
{
49-
var expression = GetOrCreateExpression<TKey, TValue>(selection);
42+
var expression = selection.ToSelectorExpression<TValue>();
5043
return dataLoader.Select(expression);
5144
}
5245

@@ -81,8 +74,8 @@ public static IPagingDataLoader<TKey, Page<TValue>> Select<TKey, TValue>(
8174
var count = GetConnectionSelections(selection, buffer);
8275
for (var i = 0; i < count; i++)
8376
{
84-
var expression = GetOrCreateExpression<TKey, TValue>(buffer[i]);
85-
HotChocolatePaginationBatchingDataLoaderExtensions.Select(dataLoader, expression);
77+
var expression = buffer[i].ToSelectorExpression<TValue>();
78+
dataLoader.Select(expression);
8679
}
8780
ArrayPool<ISelection>.Shared.Return(buffer);
8881
}
@@ -92,15 +85,15 @@ public static IPagingDataLoader<TKey, Page<TValue>> Select<TKey, TValue>(
9285
var count = GetCollectionSelections(selection, buffer);
9386
for (var i = 0; i < count; i++)
9487
{
95-
var expression = GetOrCreateExpression<TKey, TValue>(buffer[i]);
96-
HotChocolatePaginationBatchingDataLoaderExtensions.Select(dataLoader, expression);
88+
var expression = buffer[i].ToSelectorExpression<TValue>();
89+
dataLoader.Select(expression);
9790
}
9891
ArrayPool<ISelection>.Shared.Return(buffer);
9992
}
10093
else
10194
{
102-
var expression = GetOrCreateExpression<TKey, TValue>(selection);
103-
HotChocolatePaginationBatchingDataLoaderExtensions.Select(dataLoader, expression);
95+
var expression = selection.ToSelectorExpression<TValue>();
96+
dataLoader.Select(expression);
10497
}
10598

10699
return dataLoader;
@@ -167,45 +160,4 @@ private static int GetCollectionSelections(ISelection selection, Span<ISelection
167160

168161
return count;
169162
}
170-
171-
private static Expression<Func<TValue, TValue>> GetOrCreateExpression<TKey, TValue>(
172-
ISelection selection)
173-
where TKey : notnull
174-
{
175-
return selection.DeclaringOperation.GetOrAddState(
176-
CreateExpressionKey(selection.Id),
177-
static (_, ctx) => ctx._builder.BuildExpression<TValue>(ctx.selection),
178-
(_builder, selection));
179-
}
180-
181-
private static string CreateExpressionKey(int key)
182-
{
183-
var keyPrefix = GetKeyPrefix();
184-
var requiredBufferSize = EstimateIntLength(key) + keyPrefix.Length;
185-
Span<byte> span = stackalloc byte[requiredBufferSize];
186-
keyPrefix.CopyTo(span);
187-
Utf8Formatter.TryFormat(key, span.Slice(keyPrefix.Length), out var written, 'D');
188-
return Encoding.UTF8.GetString(span.Slice(0, written + keyPrefix.Length));
189-
}
190-
191-
private static ReadOnlySpan<byte> GetKeyPrefix()
192-
=> "hc-dataloader-expr-"u8;
193-
194-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
195-
private static int EstimateIntLength(int value)
196-
{
197-
if (value == 0)
198-
{
199-
// to print 0 we need still 1 digit
200-
return 1;
201-
}
202-
203-
// if the number is negative we need one more digit for the sign
204-
var length = (value < 0) ? 1 : 0;
205-
206-
// we add the number of digits the number has to the length of the number.
207-
length += (int)Math.Floor(Math.Log10(Math.Abs(value)) + 1);
208-
209-
return length;
210-
}
211163
}

0 commit comments

Comments
 (0)