Skip to content

Commit 3fc22fe

Browse files
committed
Add LinqExpressionOptions for string comparison control
1 parent 907b46e commit 3fc22fe

7 files changed

Lines changed: 149 additions & 2077 deletions

File tree

src/LoreSoft.Blazor.Controls/Extensions/QueryExtensions.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ public static IQueryable<T> Sort<T>(this IQueryable<T> query, IEnumerable<DataSo
6464
/// <typeparam name="T">The type of the elements in the query.</typeparam>
6565
/// <param name="query">The source query.</param>
6666
/// <param name="filter">The filter rule to apply.</param>
67+
/// <param name="options">The options to use when building the filter expression.</param>
6768
/// <returns>The filtered query, or the original query if <paramref name="filter"/> is <c>null</c> or empty.</returns>
68-
public static IQueryable<T> Filter<T>(this IQueryable<T> query, QueryRule? filter)
69+
public static IQueryable<T> Filter<T>(this IQueryable<T> query, QueryRule? filter, LinqExpressionOptions? options = null)
6970
{
7071
if (filter is null)
7172
return query;
@@ -74,7 +75,7 @@ public static IQueryable<T> Filter<T>(this IQueryable<T> query, QueryRule? filte
7475

7576
return LinqExpressionBuilder.Pool.Use(builder =>
7677
{
77-
builder.Build(filter);
78+
builder.Build(filter, options);
7879

7980
var predicate = builder.Expression;
8081
var parameters = builder.Parameters.ToArray();
@@ -93,13 +94,14 @@ public static IQueryable<T> Filter<T>(this IQueryable<T> query, QueryRule? filte
9394
/// <typeparam name="T">The type of the elements in the query.</typeparam>
9495
/// <param name="query">The source query.</param>
9596
/// <param name="request">The data request containing filter, sort, and paging information.</param>
97+
/// <param name="options">The options to use when building the filter expression.</param>
9698
/// <returns>A <see cref="DataResult{T}"/> containing the total count and the paged, sorted, and filtered results.</returns>
97-
public static DataResult<T> DataQuery<T>(this IQueryable<T> query, DataRequest request)
99+
public static DataResult<T> DataQuery<T>(this IQueryable<T> query, DataRequest request, LinqExpressionOptions? options = null)
98100
{
99101
ArgumentNullException.ThrowIfNull(query);
100102
ArgumentNullException.ThrowIfNull(request);
101103

102-
var filterQuery = query.Filter(request.Query);
104+
var filterQuery = query.Filter(request.Query, options);
103105

104106
var total = filterQuery.Count();
105107

src/LoreSoft.Blazor.Controls/Forms/InputList.razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ private async Task UpdateItem(int index, object? value)
378378
if (index >= list.Count)
379379
return;
380380

381-
list[index] = parsedValue;
381+
list[index] = parsedValue!;
382382

383383
ClearParsingError();
384384
await NotifyValueChanged(list).ConfigureAwait(false);

src/LoreSoft.Blazor.Controls/Utilities/LinqExpressionBuilder.cs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ namespace LoreSoft.Blazor.Controls.Utilities;
1212
/// </summary>
1313
public class LinqExpressionBuilder
1414
{
15-
private static readonly Dictionary<string, Action<StringBuilder, List<object?>, QueryFilter>> _filterWriters = new(StringComparer.OrdinalIgnoreCase);
15+
private delegate void FilterWriter(StringBuilder builder, List<object?> parameters, QueryFilter filter, LinqExpressionOptions options);
16+
17+
private static readonly Dictionary<string, FilterWriter> _filterWriters = new(StringComparer.OrdinalIgnoreCase);
1618

1719
/// <summary>
1820
/// Initializes static filter writers for supported query operators.
@@ -44,7 +46,7 @@ static LinqExpressionBuilder()
4446
/// <param name="writer">The writer action to use for the operator.</param>
4547
public static void RegisterWriter(string @operator, Action<StringBuilder, List<object?>, QueryFilter> writer)
4648
{
47-
_filterWriters[@operator] = writer;
49+
_filterWriters[@operator] = (builder, parameters, filter, _) => writer(builder, parameters, filter);
4850
}
4951

5052
/// <summary>
@@ -100,6 +102,7 @@ public static bool IsValid(QueryFilter? queryFilter)
100102

101103
private readonly StringBuilder _expression = new();
102104
private readonly List<object?> _values = [];
105+
private LinqExpressionOptions _options = LinqExpressionOptions.Default;
103106

104107
/// <summary>
105108
/// Gets the list of parameters used in the built LINQ expression. Generated by <see cref="Build(QueryRule?)"/>.
@@ -118,9 +121,20 @@ public static bool IsValid(QueryFilter? queryFilter)
118121
/// </summary>
119122
/// <param name="queryRule">The query rule to build the expression from.</param>
120123
public void Build(QueryRule? queryRule)
124+
=> Build(queryRule, null);
125+
126+
/// <summary>
127+
/// Builds a LINQ expression string and parameter list from the specified <see cref="QueryRule"/>.
128+
/// Expression will be updated with the generated LINQ expression.
129+
/// Parameters will also be updated with the values used in the expression.
130+
/// </summary>
131+
/// <param name="queryRule">The query rule to build the expression from.</param>
132+
/// <param name="options">The options to use when building the expression.</param>
133+
public void Build(QueryRule? queryRule, LinqExpressionOptions? options)
121134
{
122135
_expression.Length = 0;
123136
_values.Clear();
137+
_options = options ?? LinqExpressionOptions.Default;
124138

125139
if (queryRule == null)
126140
return;
@@ -205,9 +219,9 @@ private void WriteExpression(QueryFilter filter)
205219
comparison = QueryOperators.Equal;
206220

207221
if (_filterWriters.TryGetValue(comparison, out var action))
208-
action(_expression, _values, filter);
222+
action(_expression, _values, filter, _options);
209223
else
210-
WriteStandardFilter(_expression, _values, filter);
224+
WriteStandardFilter(_expression, _values, filter, _options);
211225
}
212226

213227
/// <summary>
@@ -216,7 +230,8 @@ private void WriteExpression(QueryFilter filter)
216230
/// <param name="builder">The <see cref="StringBuilder"/> to append to.</param>
217231
/// <param name="parameters">The parameter list to add values to.</param>
218232
/// <param name="filter">The query filter to write.</param>
219-
private static void WriteStringFilter(StringBuilder builder, List<object?> parameters, QueryFilter filter)
233+
/// <param name="options">The options to use when writing the expression.</param>
234+
private static void WriteStringFilter(StringBuilder builder, List<object?> parameters, QueryFilter filter, LinqExpressionOptions options)
220235
{
221236
// Field required for expression
222237
if (string.IsNullOrWhiteSpace(filter.Field))
@@ -254,9 +269,16 @@ private static void WriteStringFilter(StringBuilder builder, List<object?> param
254269
.Append('.')
255270
.Append(method)
256271
.Append("(@")
257-
.Append(index)
258-
.Append(", StringComparison.OrdinalIgnoreCase")
259-
.Append(')');
272+
.Append(index);
273+
274+
if (options.StringComparison is { } stringComparison)
275+
{
276+
builder
277+
.Append(", StringComparison.")
278+
.Append(stringComparison);
279+
}
280+
281+
builder.Append(')');
260282

261283
parameters.Add(value);
262284
}
@@ -267,7 +289,8 @@ private static void WriteStringFilter(StringBuilder builder, List<object?> param
267289
/// <param name="builder">The <see cref="StringBuilder"/> to append to.</param>
268290
/// <param name="parameters">The parameter list to add values to.</param>
269291
/// <param name="filter">The query filter to write.</param>
270-
private static void WriteStandardFilter(StringBuilder builder, List<object?> parameters, QueryFilter filter)
292+
/// <param name="options">The options to use when writing the expression.</param>
293+
private static void WriteStandardFilter(StringBuilder builder, List<object?> parameters, QueryFilter filter, LinqExpressionOptions options)
271294
{
272295
// Field required for expression
273296
if (string.IsNullOrWhiteSpace(filter.Field))
@@ -305,7 +328,8 @@ private static void WriteStandardFilter(StringBuilder builder, List<object?> par
305328
/// <param name="builder">The <see cref="StringBuilder"/> to append to.</param>
306329
/// <param name="parameters">The parameter list to add values to.</param>
307330
/// <param name="filter">The query filter to write.</param>
308-
private static void WriteNullFilter(StringBuilder builder, List<object?> parameters, QueryFilter filter)
331+
/// <param name="options">The options to use when writing the expression.</param>
332+
private static void WriteNullFilter(StringBuilder builder, List<object?> parameters, QueryFilter filter, LinqExpressionOptions options)
309333
{
310334
// Field required for expression
311335
if (string.IsNullOrWhiteSpace(filter.Field))
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace LoreSoft.Blazor.Controls.Utilities;
2+
3+
/// <summary>
4+
/// Provides options for building dynamic LINQ expressions.
5+
/// </summary>
6+
public class LinqExpressionOptions
7+
{
8+
/// <summary>
9+
/// Gets the default LINQ expression options.
10+
/// </summary>
11+
public static LinqExpressionOptions Default => new();
12+
13+
/// <summary>
14+
/// Gets LINQ expression options with no optional provider-specific features enabled.
15+
/// </summary>
16+
public static LinqExpressionOptions Empty => new() { StringComparison = null };
17+
18+
/// <summary>
19+
/// Gets or sets the string comparison to use for string filter methods.
20+
/// Set to <c>null</c> to emit provider-compatible string methods without a comparison argument.
21+
/// </summary>
22+
public StringComparison? StringComparison { get; set; } = System.StringComparison.OrdinalIgnoreCase;
23+
}

test/LoreSoft.Blazor.Controls.Tests/PublicApiTest.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)