Skip to content

Commit 3d8f4f7

Browse files
committed
Refinements
1 parent ed9f54f commit 3d8f4f7

7 files changed

+304
-297
lines changed

src/GreenDonut/src/Core/Data/PredicateDataLoaderExtensions.cs renamed to src/GreenDonut/src/Core/Data/GreenDonutPredicateDataLoaderExtensions.cs

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace GreenDonut.Data;
66
/// <summary>
77
/// Data loader extensions for predicates.
88
/// </summary>
9-
public static class PredicateDataLoaderExtensions
9+
public static class GreenDonutPredicateDataLoaderExtensions
1010
{
1111
/// <summary>
1212
/// Branches a DataLoader and applies a predicate to filter the data.
@@ -88,48 +88,6 @@ public static IDataLoader<TKey, List<TValue>> Where<TKey, TValue>(
8888
return (IQueryDataLoader<TKey, List<TValue>>)dataLoader.Branch(branchKey, DataStateHelper.CreateBranch, state);
8989
}
9090

91-
/// <summary>
92-
/// Applies the predicate from the DataLoader state to a queryable.
93-
/// </summary>
94-
/// <param name="query">
95-
/// The queryable to apply the predicate to.
96-
/// </param>
97-
/// <param name="builder">
98-
/// The predicate builder.
99-
/// </param>
100-
/// <typeparam name="T">
101-
/// The queryable type.
102-
/// </typeparam>
103-
/// <returns>
104-
/// Returns a query with the predicate applied, ready to fetch data with the key.
105-
/// </returns>
106-
/// <exception cref="ArgumentNullException">
107-
/// Throws if <paramref name="query"/> is <c>null</c>.
108-
/// </exception>
109-
public static IQueryable<T> Where<T>(
110-
this IQueryable<T> query,
111-
IPredicateBuilder builder)
112-
{
113-
if (query is null)
114-
{
115-
throw new ArgumentNullException(nameof(query));
116-
}
117-
118-
if (builder is null)
119-
{
120-
throw new ArgumentNullException(nameof(builder));
121-
}
122-
123-
var predicate = builder.TryCompile<T>();
124-
125-
if (predicate is not null)
126-
{
127-
query = query.Where(predicate);
128-
}
129-
130-
return query;
131-
}
132-
13391
internal static DefaultPredicateBuilder GetOrCreateBuilder<TValue>(
13492
IImmutableDictionary<string, object?> contextData,
13593
Expression<Func<TValue, bool>> predicate)

src/GreenDonut/src/Core/Data/QueryContextDataLoaderExtensions.cs renamed to src/GreenDonut/src/Core/Data/GreenDonutQueryContextDataLoaderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace GreenDonut.Data;
22

3-
public static class QueryContextDataLoaderExtensions
3+
public static class GreenDonutQueryContextDataLoaderExtensions
44
{
55
public static IDataLoader<TKey, TValue> With<TKey, TValue>(
66
this IDataLoader<TKey, TValue> dataLoader,

src/GreenDonut/src/Core/Data/SelectionDataLoaderExtensions.cs renamed to src/GreenDonut/src/Core/Data/GreenDonutQueryableExtensions.cs

Lines changed: 119 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,189 +1,23 @@
11
using System.Linq.Expressions;
22
using System.Reflection;
3+
using GreenDonut.Data;
34
using static GreenDonut.ExpressionHelpers;
45

5-
namespace GreenDonut.Data;
6+
// ReSharper disable once CheckNamespace
7+
namespace System.Linq;
68

79
/// <summary>
8-
/// Data loader extensions for projections.
10+
/// Provides extension methods to integrate <see cref="IQueryable{T}"/>
11+
/// for <see cref="SortDefinition{T}"/> and <see cref="QueryContext{T}"/>.
912
/// </summary>
10-
public static class SelectionDataLoaderExtensions
13+
public static class GreenDonutQueryableExtensions
1114
{
1215
private static readonly MethodInfo _selectMethod =
1316
typeof(Enumerable)
1417
.GetMethods()
1518
.Where(m => m.Name == nameof(Enumerable.Select) && m.GetParameters().Length == 2)
1619
.First(m => m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>));
1720

18-
/// <summary>
19-
/// Branches a DataLoader and applies a selector to load the data.
20-
/// </summary>
21-
/// <param name="dataLoader">
22-
/// The DataLoader to branch.
23-
/// </param>
24-
/// <param name="selector">
25-
/// The data selector.
26-
/// </param>
27-
/// <typeparam name="TKey">
28-
/// The key type.
29-
/// </typeparam>
30-
/// <typeparam name="TValue">
31-
/// The value type.
32-
/// </typeparam>
33-
/// <returns>
34-
/// Returns a branched DataLoader with the selector applied.
35-
/// </returns>
36-
/// <exception cref="ArgumentNullException">
37-
/// Throws if <paramref name="dataLoader"/> is <c>null</c>.
38-
/// </exception>
39-
public static IDataLoader<TKey, TValue> Select<TKey, TValue>(
40-
this IDataLoader<TKey, TValue> dataLoader,
41-
Expression<Func<TValue, TValue>>? selector)
42-
where TKey : notnull
43-
{
44-
if (dataLoader is null)
45-
{
46-
throw new ArgumentNullException(nameof(dataLoader));
47-
}
48-
49-
if (selector is null)
50-
{
51-
return dataLoader;
52-
}
53-
54-
if (dataLoader.ContextData.TryGetValue(DataStateKeys.Selector, out var value))
55-
{
56-
var context = (DefaultSelectorBuilder)value!;
57-
context.Add(selector);
58-
return dataLoader;
59-
}
60-
61-
var branchKey = selector.ComputeHash();
62-
var state = new QueryState(DataStateKeys.Selector, new DefaultSelectorBuilder(selector));
63-
return (IQueryDataLoader<TKey, TValue>)dataLoader.Branch(branchKey, DataStateHelper.CreateBranch, state);
64-
}
65-
66-
public static IDataLoader<TKey, TValue[]> Select<TKey, TValue>(
67-
this IDataLoader<TKey, TValue[]> dataLoader,
68-
Expression<Func<TValue, TValue>>? selector)
69-
where TKey : notnull
70-
{
71-
if (dataLoader is null)
72-
{
73-
throw new ArgumentNullException(nameof(dataLoader));
74-
}
75-
76-
if (selector is null)
77-
{
78-
return dataLoader;
79-
}
80-
81-
if (dataLoader.ContextData.TryGetValue(DataStateKeys.Selector, out var value))
82-
{
83-
var context = (DefaultSelectorBuilder)value!;
84-
context.Add(selector);
85-
return dataLoader;
86-
}
87-
88-
var branchKey = selector.ComputeHash();
89-
var state = new QueryState(DataStateKeys.Selector, new DefaultSelectorBuilder(selector));
90-
return (IQueryDataLoader<TKey, TValue[]>)dataLoader.Branch(branchKey, DataStateHelper.CreateBranch, state);
91-
}
92-
93-
public static IDataLoader<TKey, List<TValue>> Select<TKey, TValue>(
94-
this IDataLoader<TKey, List<TValue>> dataLoader,
95-
Expression<Func<TValue, TValue>>? selector)
96-
where TKey : notnull
97-
{
98-
if (dataLoader is null)
99-
{
100-
throw new ArgumentNullException(nameof(dataLoader));
101-
}
102-
103-
if (selector is null)
104-
{
105-
return dataLoader;
106-
}
107-
108-
if (dataLoader.ContextData.TryGetValue(DataStateKeys.Selector, out var value))
109-
{
110-
var context = (DefaultSelectorBuilder)value!;
111-
context.Add(selector);
112-
return dataLoader;
113-
}
114-
115-
var branchKey = selector.ComputeHash();
116-
var state = new QueryState(DataStateKeys.Selector, new DefaultSelectorBuilder(selector));
117-
return (IQueryDataLoader<TKey, List<TValue>>)dataLoader.Branch(branchKey, DataStateHelper.CreateBranch, state);
118-
}
119-
120-
/// <summary>
121-
/// Includes a property in the query.
122-
/// </summary>
123-
/// <param name="dataLoader">
124-
/// The DataLoader to include the property in.
125-
/// </param>
126-
/// <param name="includeSelector">
127-
/// The property selector.
128-
/// </param>
129-
/// <typeparam name="TKey">
130-
/// The key type.
131-
/// </typeparam>
132-
/// <typeparam name="TValue">
133-
/// The value type.
134-
/// </typeparam>
135-
/// <returns>
136-
/// Returns the DataLoader with the property included.
137-
/// </returns>
138-
/// <exception cref="ArgumentNullException">
139-
/// Throws if <paramref name="dataLoader"/> is <c>null</c>.
140-
/// </exception>
141-
/// <exception cref="ArgumentException">
142-
/// Throws if the include selector is not a property selector.
143-
/// </exception>
144-
public static IDataLoader<TKey, TValue> Include<TKey, TValue>(
145-
this IDataLoader<TKey, TValue> dataLoader,
146-
Expression<Func<TValue, object?>> includeSelector)
147-
where TKey : notnull
148-
{
149-
if (dataLoader is null)
150-
{
151-
throw new ArgumentNullException(nameof(dataLoader));
152-
}
153-
154-
if(!dataLoader.ContextData.ContainsKey(DataStateKeys.Selector))
155-
{
156-
throw new InvalidOperationException(
157-
"The Include method must be called after the Select method.");
158-
}
159-
160-
if (includeSelector is null)
161-
{
162-
throw new ArgumentNullException(nameof(includeSelector));
163-
}
164-
165-
if (includeSelector is not LambdaExpression lambda)
166-
{
167-
throw new ArgumentException(
168-
"The include selector must be a lambda expression.",
169-
nameof(includeSelector));
170-
}
171-
172-
if (lambda.Body is not MemberExpression member
173-
|| member.Member.MemberType != MemberTypes.Property)
174-
{
175-
throw new ArgumentException(
176-
"The include selector must be a property selector.",
177-
nameof(includeSelector));
178-
}
179-
180-
var context = dataLoader.GetOrSetState(
181-
DataStateKeys.Selector,
182-
_ => new DefaultSelectorBuilder());
183-
context.Add(Rewrite(includeSelector));
184-
return dataLoader;
185-
}
186-
18721
/// <summary>
18822
/// Applies the selector from the DataLoader state to a queryable.
18923
/// </summary>
@@ -343,6 +177,119 @@ public static IQueryable<KeyValueResult<TKey, IEnumerable<TValue>>> Select<T, TK
343177
return query.Select(keyValueSelectorExpr);
344178
}
345179

180+
/// <summary>
181+
/// Applies the predicate from the DataLoader state to a queryable.
182+
/// </summary>
183+
/// <param name="query">
184+
/// The queryable to apply the predicate to.
185+
/// </param>
186+
/// <param name="builder">
187+
/// The predicate builder.
188+
/// </param>
189+
/// <typeparam name="T">
190+
/// The queryable type.
191+
/// </typeparam>
192+
/// <returns>
193+
/// Returns a query with the predicate applied, ready to fetch data with the key.
194+
/// </returns>
195+
/// <exception cref="ArgumentNullException">
196+
/// Throws if <paramref name="query"/> is <c>null</c>.
197+
/// </exception>
198+
public static IQueryable<T> Where<T>(
199+
this IQueryable<T> query,
200+
IPredicateBuilder builder)
201+
{
202+
if (query is null)
203+
{
204+
throw new ArgumentNullException(nameof(query));
205+
}
206+
207+
if (builder is null)
208+
{
209+
throw new ArgumentNullException(nameof(builder));
210+
}
211+
212+
var predicate = builder.TryCompile<T>();
213+
214+
if (predicate is not null)
215+
{
216+
query = query.Where(predicate);
217+
}
218+
219+
return query;
220+
}
221+
222+
public static IQueryable<T> Order<T>(this IQueryable<T> queryable, SortDefinition<T>? sortDefinition)
223+
{
224+
if (queryable is null)
225+
{
226+
throw new ArgumentNullException(nameof(queryable));
227+
}
228+
229+
if (sortDefinition is null || sortDefinition.Operations.Length == 0)
230+
{
231+
return queryable;
232+
}
233+
234+
var first = sortDefinition.Operations[0];
235+
var query = first.ApplyOrderBy(queryable);
236+
237+
for (var i = 1; i < sortDefinition.Operations.Length; i++)
238+
{
239+
query = sortDefinition.Operations[i].ApplyThenBy(query);
240+
}
241+
242+
return query;
243+
}
244+
245+
/// <summary>
246+
/// Applies a data context to the queryable.
247+
/// </summary>
248+
/// <param name="queryable">
249+
/// The queryable that shall be projected, filtered and sorted.
250+
/// </param>
251+
/// <param name="queryContext">
252+
/// The data context that shall be applied to the queryable.
253+
/// </param>
254+
/// <typeparam name="T">
255+
/// The type of the queryable.
256+
/// </typeparam>
257+
/// <returns>
258+
/// Returns a queryable that has the data context applied.
259+
/// </returns>
260+
/// <exception cref="ArgumentNullException">
261+
/// Throws if <paramref name="queryable"/> is <c>null</c> or if <paramref name="queryContext"/> is <c>null</c>.
262+
/// </exception>
263+
public static IQueryable<T> Apply<T>(this IQueryable<T> queryable, QueryContext<T>? queryContext)
264+
{
265+
if (queryable is null)
266+
{
267+
throw new ArgumentNullException(nameof(queryable));
268+
}
269+
270+
if (queryContext is null)
271+
{
272+
return queryable;
273+
}
274+
275+
if (queryContext.Predicate is not null)
276+
{
277+
queryable = queryable.Where(queryContext.Predicate);
278+
}
279+
280+
if (queryContext.Sorting is not null)
281+
{
282+
queryable = queryable.Order(queryContext.Sorting);
283+
}
284+
285+
if (queryContext.Selector is not null)
286+
{
287+
queryable = queryable.Select(queryContext.Selector);
288+
}
289+
290+
return queryable;
291+
}
292+
346293
private static Expression<T> ReplaceParameter<T>(
347294
Expression<T> expression,
348295
ParameterExpression oldParameter,

0 commit comments

Comments
 (0)