generated from Avanade/avanade-template
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathIBoundClientExtensions.cs
114 lines (102 loc) · 6.04 KB
/
IBoundClientExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx
using CoreEx.Wildcards;
using Simple.OData.Client;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace CoreEx.OData
{
/// <summary>
/// Adds additional extension methods to the <see cref="IBoundClient{T}"/>.
/// </summary>
public static class IBoundClientExtensions
{
/// <summary>
/// Filters a sequence of values based on a <paramref name="predicate"/> only <paramref name="when"/> <c>true</c>.
/// </summary>
/// <typeparam name="TElement">The element <see cref="Type"/>.</typeparam>
/// <param name="query">The query.</param>
/// <param name="when">Indicates to perform an underlying <see cref="IFluentClient{TElement, FT}.Filter(Expression{Func{TElement, bool}})"/> only when <c>true</c>;
/// otherwise, no <b>Where</b> is invoked.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <returns>The resulting query.</returns>
public static IBoundClient<TElement> FilterWhen<TElement>(this IBoundClient<TElement> query, bool when, Expression<Func<TElement, bool>> predicate) where TElement : class
{
var q = query.ThrowIfNull(nameof(query));
if (when)
return q.Filter(predicate.ThrowIfNull(nameof(predicate)));
else
return q;
}
/// <summary>
/// Filters a sequence of values based on a <paramref name="predicate"/> only when the <paramref name="with"/> is not the default value for the <see cref="Type"/>.
/// </summary>
/// <typeparam name="TElement">The element <see cref="Type"/>.</typeparam>
/// <typeparam name="T">The with value <see cref="Type"/>.</typeparam>
/// <param name="query">The query.</param>
/// <param name="with">Indicates to perform an underlying <see cref="IFluentClient{TElement, FT}.Filter(Expression{Func{TElement, bool}})"/> only when the with is not the default
/// value; otherwise, no <b>Where</b> is invoked.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <returns>The resulting query.</returns>
public static IBoundClient<TElement> FilterWith<TElement, T>(this IBoundClient<TElement> query, T with, Expression<Func<TElement, bool>> predicate) where TElement : class
{
var q = query.ThrowIfNull(nameof(query));
if (Comparer<T>.Default.Compare(with, default!) != 0 && Comparer<T>.Default.Compare(with, default!) != 0)
{
if (with is not string && with is System.Collections.IEnumerable ie && !ie.GetEnumerator().MoveNext())
return q;
return q.Filter(predicate.ThrowIfNull(nameof(predicate)));
}
else
return q;
}
/// <summary>
/// Filters a sequence of values using the specified <paramref name="property"/> and <paramref name="text"/> containing <see cref="Wildcard.MultiBasic"/> supported wildcards.
/// </summary>
/// <typeparam name="TElement">The element <see cref="Type"/>.</typeparam>
/// <param name="query">The query.</param>
/// <param name="property">The <see cref="MemberExpression"/>.</param>
/// <param name="text">The text to query.</param>
/// <param name="ignoreCase">Indicates whether the comparison should ignore case (default) or not; will use <see cref="string.ToUpper()"/> when selected for comparisons.</param>
/// <param name="checkForNull">Indicates whether a null check should also be performed before the comparion occurs (defaults to <c>true</c>).</param>
/// <returns>The resulting (updated) query.</returns>
public static IBoundClient<TElement> FilterWildcard<TElement>(this IBoundClient<TElement> query, Expression<Func<TElement, string?>> property, string? text, bool ignoreCase = true, bool checkForNull = true) where TElement : class
{
var q = query.ThrowIfNull(nameof(query));
var p = property.ThrowIfNull(nameof(property));
// Check the expression.
if (p.Body is not MemberExpression me)
throw new ArgumentException("Property expression must be of Type MemberExpression.", nameof(property));
Expression exp = me;
var wc = Wildcard.MultiBasic;
var wr = wc.Parse(text).ThrowOnError();
// Exit stage left where nothing to do.
if (wr.Selection.HasFlag(WildcardSelection.None) || wr.Selection.HasFlag(WildcardSelection.Single))
return query;
var s = wr.GetTextWithoutWildcards();
if (ignoreCase)
{
s = s?.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
exp = Expression.Call(me, typeof(string).GetMethod(nameof(string.ToUpper), Type.EmptyTypes)!);
}
if (wr.Selection.HasFlag(WildcardSelection.Equal))
exp = Expression.Equal(exp, Expression.Constant(s));
else if (wr.Selection.HasFlag(WildcardSelection.EndsWith))
exp = Expression.Call(exp, "EndsWith", null, Expression.Constant(s));
else if (wr.Selection.HasFlag(WildcardSelection.StartsWith))
exp = Expression.Call(exp, "StartsWith", null, Expression.Constant(s));
else if (wr.Selection.HasFlag(WildcardSelection.Contains))
exp = Expression.Call(exp, "Contains", null, Expression.Constant(s));
else
throw new ArgumentException("Wildcard selection text is not supported.", nameof(text));
// Add check for not null.
if (checkForNull)
{
var ee = Expression.NotEqual(me, Expression.Constant(null));
exp = Expression.AndAlso(ee, exp);
}
var le = Expression.Lambda<Func<TElement, bool>>(exp, p.Parameters);
return q.Filter(le);
}
}
}