Skip to content

Commit b271436

Browse files
authored
Fixed issue with extracting predicates from IFilterContext. (#7693)
1 parent 8196b08 commit b271436

8 files changed

+275
-63
lines changed

src/HotChocolate/Data/src/Data/Filters/Context/FilterContext.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public void Handled(bool isHandled)
6767
return null;
6868
}
6969

70-
private object? Serialize(IFilterValueNode? value)
70+
private static object? Serialize(IFilterValueNode? value)
7171
{
7272
switch (value)
7373
{

src/HotChocolate/Data/src/Data/Filters/Context/FilterContextResolverContextExtensions.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ context.LocalContextData[ContextValueNodeKey] is IValueNode node
3333
return null;
3434
}
3535

36-
FilterContext filterContext =
37-
new(context, filterInput, filter, context.Service<InputParser>());
36+
var filterContext = new FilterContext(context, filterInput, filter, context.Service<InputParser>());
3837

3938
// disable the execution of filtering by default
4039
filterContext.Handled(true);

src/HotChocolate/Data/src/Data/Filters/Expressions/QueryableFilterProvider.cs

+13-7
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,18 @@ private ApplyFiltering CreateApplicator<TEntityType>(string argumentName)
184184
=> (context, input) =>
185185
{
186186
var inMemory = IsInMemoryQuery<TEntityType>(input);
187+
188+
// if no filter is defined we can stop here and yield back control.
189+
var skipFiltering = context.GetLocalStateOrDefault<bool>(SkipFilteringKey);
190+
191+
// ensure filtering is only applied once
192+
context.SetLocalState(SkipFilteringKey, true);
193+
194+
if (skipFiltering)
195+
{
196+
return input;
197+
}
198+
187199
var predicate = AsPredicate<TEntityType>(context, argumentName, inMemory);
188200

189201
if (predicate is not null)
@@ -205,13 +217,7 @@ private ApplyFiltering CreateApplicator<TEntityType>(string argumentName)
205217
var filter = context.GetLocalStateOrDefault<IValueNode>(ContextValueNodeKey) ??
206218
context.ArgumentLiteral<IValueNode>(argumentName);
207219

208-
// if no filter is defined we can stop here and yield back control.
209-
var skipFiltering = context.GetLocalStateOrDefault<bool>(SkipFilteringKey);
210-
211-
// ensure filtering is only applied once
212-
context.SetLocalState(SkipFilteringKey, true);
213-
214-
if (filter.IsNull() || skipFiltering)
220+
if (filter.IsNull())
215221
{
216222
return null;
217223
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using HotChocolate.Data.Filters;
2+
using HotChocolate.Execution.Processing;
3+
4+
// ReSharper disable once CheckNamespace
5+
namespace System.Linq;
6+
7+
/// <summary>
8+
/// Provides extension methods to integrate <see cref="IQueryable{T}"/>
9+
/// with <see cref="ISelection"/> and <see cref="IFilterContext"/>.
10+
/// </summary>
11+
public static class QueryableExtensions
12+
{
13+
/// <summary>
14+
/// Applies a selection to the queryable.
15+
/// </summary>
16+
/// <param name="queryable">
17+
/// The queryable that shall be projected.
18+
/// </param>
19+
/// <param name="selection">
20+
/// The selection that shall be applied to the queryable.
21+
/// </param>
22+
/// <typeparam name="T">
23+
/// The type of the queryable.
24+
/// </typeparam>
25+
/// <returns>
26+
/// Returns a queryable that has the selection applied.
27+
/// </returns>
28+
/// <exception cref="ArgumentNullException">
29+
/// Throws if <paramref name="queryable"/> is <c>null</c> or if <paramref name="selection"/> is <c>null</c>.
30+
/// </exception>
31+
public static IQueryable<T> Select<T>(this IQueryable<T> queryable, ISelection selection)
32+
{
33+
if (queryable is null)
34+
{
35+
throw new ArgumentNullException(nameof(queryable));
36+
}
37+
38+
if (selection is null)
39+
{
40+
throw new ArgumentNullException(nameof(selection));
41+
}
42+
43+
return queryable.Select(selection.AsSelector<T>());
44+
}
45+
46+
/// <summary>
47+
/// Applies a filter context to the queryable.
48+
/// </summary>
49+
/// <param name="queryable">
50+
/// The queryable that shall be filtered.
51+
/// </param>
52+
/// <param name="filter">
53+
/// The filter context that shall be applied to the queryable.
54+
/// </param>
55+
/// <typeparam name="T">
56+
/// The type of the queryable.
57+
/// </typeparam>
58+
/// <returns>
59+
/// Returns a queryable that has the filter applied.
60+
/// </returns>
61+
/// <exception cref="ArgumentNullException">
62+
/// Throws if <paramref name="queryable"/> is <c>null</c> or if <paramref name="filter"/> is <c>null</c>.
63+
/// </exception>
64+
public static IQueryable<T> Where<T>(this IQueryable<T> queryable, IFilterContext filter)
65+
{
66+
if (queryable is null)
67+
{
68+
throw new ArgumentNullException(nameof(queryable));
69+
}
70+
71+
if (filter is null)
72+
{
73+
throw new ArgumentNullException(nameof(filter));
74+
}
75+
76+
var predicate = filter.AsPredicate<T>();
77+
return predicate is null ? queryable : queryable.Where(predicate);
78+
}
79+
}

src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<AssemblyName>HotChocolate.Data</AssemblyName>
66
<RootNamespace>HotChocolate.Data</RootNamespace>
77
<Description>Contains ready to use extensions for data management in HotChocolate. This includes filtering, projections and sorting</Description>
8-
<NoWarn>HC8001;$(NoWarn)</NoWarn>
8+
<NoWarn>HC8001;GD0001;$(NoWarn)</NoWarn>
99
</PropertyGroup>
1010

1111
<ItemGroup>

0 commit comments

Comments
 (0)