Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/BootstrapBlazor.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>10.2.0</Version>
<Version>10.2.1-beta01</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
58 changes: 32 additions & 26 deletions src/BootstrapBlazor/Extensions/LambdaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,27 @@ private class ComboExpressionVisitor(ParameterExpression parameter) : Expression
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="filter"></param>
/// <returns></returns>
public static Func<TItem, bool> GetFilterFunc<TItem>(this FilterKeyValueAction filter) => filter.GetFilterLambda<TItem>().Compile();
/// <param name="comparison"><see cref="StringComparison"/> 实例,此方法不支持 EFCore Where 查询</param>
public static Func<TItem, bool> GetFilterFunc<TItem>(this FilterKeyValueAction filter, StringComparison? comparison = null) => filter.GetFilterLambda<TItem>(comparison).Compile();
Comment on lines +37 to +38
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing return value documentation. The parameter documentation was added, but the <returns> section was removed. Add: /// <returns></returns> to maintain complete API documentation.

Copilot uses AI. Check for mistakes.

/// <summary>
/// 指定 FilterKeyValueAction 获取 Lambda 表达式
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="filter"></param>
/// <returns></returns>
public static Expression<Func<TItem, bool>> GetFilterLambda<TItem>(this FilterKeyValueAction filter)
/// <param name="comparison"><see cref="StringComparison"/> 实例,此方法不支持 EFCore Where 查询</param>
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing return value documentation. The parameter documentation was added, but the <returns> section was removed. Add: /// <returns></returns> to maintain complete API documentation.

Suggested change
/// <param name="comparison"><see cref="StringComparison"/> 实例,此方法不支持 EFCore Where 查询</param>
/// <param name="comparison"><see cref="StringComparison"/> 实例,此方法不支持 EFCore Where 查询</param>
/// <returns></returns>

Copilot uses AI. Check for mistakes.
public static Expression<Func<TItem, bool>> GetFilterLambda<TItem>(this FilterKeyValueAction filter, StringComparison? comparison = null)
{
var express = new List<Expression<Func<TItem, bool>>>();
if (filter.Filters.Count > 0)
{
express.AddRange(filter.Filters.Select(f => f.Filters.Count > 0
? f.Filters.GetFilterLambda<TItem>(f.FilterLogic)
: f.GetInnerFilterLambda<TItem>()));
? f.Filters.GetFilterLambda<TItem>(f.FilterLogic, comparison)
: f.GetInnerFilterLambda<TItem>(comparison)));
}
else
{
express.Add(filter.GetInnerFilterLambda<TItem>());
express.Add(filter.GetInnerFilterLambda<TItem>(comparison));
}
return express.ExpressionAndLambda(filter.FilterLogic);
}
Expand Down Expand Up @@ -83,12 +83,13 @@ public static Expression<Func<TItem, bool>> GetFilterLambda<TItem>(this FilterKe
/// <typeparam name="TItem"></typeparam>
/// <param name="filters"></param>
/// <param name="logic"></param>
/// <param name="comparison"><see cref="StringComparison"/> 实例</param>
/// <returns></returns>
private static Expression<Func<TItem, bool>> GetFilterLambda<TItem>(this IEnumerable<FilterKeyValueAction> filters, FilterLogic logic)
private static Expression<Func<TItem, bool>> GetFilterLambda<TItem>(this IEnumerable<FilterKeyValueAction> filters, FilterLogic logic, StringComparison? comparison = null)
{
var express = filters.Select(filter => filter.Filters.Count > 0
? filter.Filters.GetFilterLambda<TItem>(filter.FilterLogic)
: filter.GetInnerFilterLambda<TItem>())
? filter.Filters.GetFilterLambda<TItem>(filter.FilterLogic, comparison)
: filter.GetInnerFilterLambda<TItem>(comparison))
.ToList();
return express.ExpressionAndLambda(logic);
}
Expand Down Expand Up @@ -126,13 +127,7 @@ private static Expression<Func<TItem, bool>> ExpressionAndLambda<TItem>(this IEn
return ret ?? (r => true);
}

/// <summary>
/// 指定 FilterKeyValueAction 获取 Lambda 表达式
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="filter"></param>
/// <returns></returns>
private static Expression<Func<TItem, bool>> GetInnerFilterLambda<TItem>(this FilterKeyValueAction filter)
private static Expression<Func<TItem, bool>> GetInnerFilterLambda<TItem>(this FilterKeyValueAction filter, StringComparison? comparison = null)
{
Expression<Func<TItem, bool>> ret = t => true;
var type = typeof(TItem);
Expand All @@ -148,7 +143,7 @@ Expression<Func<TItem, bool>> GetSimpleFilterExpression()
var prop = typeof(TItem).GetPropertyByName(filter.FieldKey) ?? throw new InvalidOperationException($"the model {type.Name} not found the property {filter.FieldKey}");
var parameter = Expression.Parameter(type);
var fieldExpression = Expression.Property(parameter, prop);
ret = filter.GetFilterExpression<TItem>(prop, fieldExpression, parameter);
ret = filter.GetFilterExpression<TItem>(prop, fieldExpression, parameter, comparison);
return ret;
}

Expand All @@ -175,13 +170,13 @@ Expression<Func<TItem, bool>> GetComplexFilterExpression()

if (fieldExpression != null)
{
ret = filter.GetFilterExpression<TItem>(prop, fieldExpression, parameter);
ret = filter.GetFilterExpression<TItem>(prop, fieldExpression, parameter, comparison);
}
return ret;
}
}

private static Expression<Func<TItem, bool>> GetFilterExpression<TItem>(this FilterKeyValueAction filter, PropertyInfo? prop, Expression fieldExpression, ParameterExpression parameter)
private static Expression<Func<TItem, bool>> GetFilterExpression<TItem>(this FilterKeyValueAction filter, PropertyInfo? prop, Expression fieldExpression, ParameterExpression parameter, StringComparison? comparison = null)
{
var isNullable = false;
var eq = fieldExpression;
Expand All @@ -201,12 +196,12 @@ private static Expression<Func<TItem, bool>> GetFilterExpression<TItem>(this Fil
}
}
eq = isNullable
? Expression.AndAlso(Expression.NotEqual(fieldExpression, Expression.Constant(null)), filter.GetExpression(eq))
: filter.GetExpression(eq);
? Expression.AndAlso(Expression.NotEqual(fieldExpression, Expression.Constant(null)), filter.GetExpression(eq, comparison))
: filter.GetExpression(eq, comparison);
return Expression.Lambda<Func<TItem, bool>>(eq, parameter);
}

private static Expression GetExpression(this FilterKeyValueAction filter, Expression left)
private static Expression GetExpression(this FilterKeyValueAction filter, Expression left, StringComparison? comparison = null)
{
var right = Expression.Constant(filter.FieldValue);
return filter.FilterAction switch
Expand All @@ -217,8 +212,8 @@ private static Expression GetExpression(this FilterKeyValueAction filter, Expres
FilterAction.GreaterThanOrEqual => Expression.GreaterThanOrEqual(left, right),
FilterAction.LessThan => Expression.LessThan(left, right),
FilterAction.LessThanOrEqual => Expression.LessThanOrEqual(left, right),
FilterAction.Contains => left.Contains(right),
FilterAction.NotContains => Expression.Not(left.Contains(right)),
FilterAction.Contains => left.Contains(right, comparison),
FilterAction.NotContains => Expression.Not(left.Contains(right, comparison)),
_ => filter.FieldValue switch
{
LambdaExpression t => Expression.Invoke(t, left),
Expand All @@ -228,12 +223,23 @@ private static Expression GetExpression(this FilterKeyValueAction filter, Expres
};
}

private static BinaryExpression Contains(this Expression left, Expression right)
private static BinaryExpression Contains(this Expression left, Expression right, StringComparison? comparison) => comparison.HasValue
? ContainsWidthComparison(left, right, comparison.Value)
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method name has a typo: "ContainsWidthComparison" should be "ContainsWithComparison". "Width" refers to dimensions/size, while "With" is the correct preposition indicating inclusion.

Copilot uses AI. Check for mistakes.
: ContainsWithoutComparison(left, right);

private static BinaryExpression ContainsWithoutComparison(this Expression left, Expression right)
{
var method = typeof(string).GetMethod("Contains", [typeof(string)])!;
return Expression.AndAlso(Expression.NotEqual(left, Expression.Constant(null)), Expression.Call(left, method, right));
}

private static BinaryExpression ContainsWidthComparison(this Expression left, Expression right, StringComparison comparison)
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method name has a typo: "ContainsWidthComparison" should be "ContainsWithComparison". "Width" refers to dimensions/size, while "With" is the correct preposition indicating inclusion.

Copilot uses AI. Check for mistakes.
{
var method = typeof(string).GetMethod("Contains", [typeof(string), typeof(StringComparison)])!;
var comparisonConstant = Expression.Constant(comparison);
return Expression.AndAlso(Expression.NotEqual(left, Expression.Constant(null)), Expression.Call(left, method, right, comparisonConstant));
}

#region Count
/// <summary>
/// Count 方法内部使用 Lambda 表达式做通用适配 可接受 IEnumerable 与 Array 子类
Expand Down
8 changes: 5 additions & 3 deletions src/BootstrapBlazor/Extensions/QueryPageOptionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
Expand Down Expand Up @@ -56,15 +56,17 @@ public static FilterKeyValueAction ToFilter(this QueryPageOptions option)
/// 将 QueryPageOptions 过滤条件转换为 where 条件中的参数 <see cref="Func{T, TResult}"/>"/> 推荐 Linq 使用
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation comment contains a typo with an extra closing quote and forward slash. The text should be: '将 QueryPageOptions 过滤条件转换为 where 条件中的参数 推荐 Linq 使用' (removing the extra closing quote after the closing tag).

Copilot uses AI. Check for mistakes.
/// </summary>
/// <param name="option"></param>
/// <param name="comparison"><see cref="StringComparison"/> 实例,此方法不支持 EFCore Where 查询</param>
/// <returns></returns>
public static Func<TItem, bool> ToFilterFunc<TItem>(this QueryPageOptions option) => option.ToFilterLambda<TItem>().Compile();
public static Func<TItem, bool> ToFilterFunc<TItem>(this QueryPageOptions option, StringComparison? comparison = null) => option.ToFilterLambda<TItem>(comparison).Compile();

/// <summary>
/// 将 QueryPageOptions 过滤条件转换为 <see cref="Expression{TDelegate}"/> 表达式"/> 推荐 EFCore <see cref="IQueryable"/> 使用
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation comment contains a typo with an extra closing quote after the closing tag. The text should be: '将 QueryPageOptions 过滤条件转换为 表达式 推荐 EFCore 使用' (removing the extra "/> after 表达式).

Copilot uses AI. Check for mistakes.
/// </summary>
/// <param name="option"></param>
/// <param name="comparison"><see cref="StringComparison"/> 实例,此方法不支持 EFCore Where 查询</param>
/// <returns></returns>
public static Expression<Func<TItem, bool>> ToFilterLambda<TItem>(this QueryPageOptions option) => option.ToFilter().GetFilterLambda<TItem>();
public static Expression<Func<TItem, bool>> ToFilterLambda<TItem>(this QueryPageOptions option, StringComparison? comparison = null) => option.ToFilter().GetFilterLambda<TItem>(comparison);

/// <summary>
/// 是否包含过滤条件
Expand Down
33 changes: 26 additions & 7 deletions test/UnitTest/Extensions/LambadaExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ namespace UnitTest.Extensions;

public class LambadaExtensionsTest : BootstrapBlazorTestBase
{
[Fact]
public void GetFilterFunc_Comparison()
{
var filter = new FilterKeyValueAction()
{
FieldKey = "Name",
FilterAction = FilterAction.Contains,
FieldValue = "T"
};

var foos = new Foo[]
{
new() { Name = "Test1" },
new() { Name = "test2" },
};

var items = foos.Where(filter.GetFilterFunc<Foo>());
Assert.Single(items);

// 忽略大小写
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is in Chinese and should be translated to English for consistency with the rest of the codebase. Consider changing to: "// Case-insensitive comparison" or "// Ignore case"

Suggested change
// 忽略大小写
// Ignore case

Copilot uses AI. Check for mistakes.
items = foos.Where(filter.GetFilterFunc<Foo>(StringComparison.OrdinalIgnoreCase));
Assert.Equal(2, items.Count());
}

[Fact]
public void GetFilterFunc_Null()
{
Expand Down Expand Up @@ -694,11 +718,6 @@ public override FilterKeyValueAction GetFilterConditions()
}
}

/// <summary>
///
/// </summary>
/// <param name="fix"></param>
/// <param name="data"></param>
private class CustomDynamicData(Dictionary<string, string> data) : System.Dynamic.DynamicObject
{
/// <summary>
Expand All @@ -719,9 +738,9 @@ public CustomDynamicData() : this([]) { }
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object? result)
{
if (Dynamic.ContainsKey(binder.Name))
if (Dynamic.TryGetValue(binder.Name, out string? value))
{
result = Dynamic[binder.Name];
result = value;
}
else
{
Expand Down
5 changes: 5 additions & 0 deletions test/UnitTest/Extensions/QueryPageOptionsExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public void ToFilter_Searches()
expected = _foos.Where(predicate);
Assert.Equal(2, expected.Count());

// 忽略大小写
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is in Chinese and should be translated to English for consistency with the rest of the codebase. Consider changing to: "// Case-insensitive comparison" or "// Ignore case"

Suggested change
// 忽略大小写
// Case-insensitive comparison

Copilot uses AI. Check for mistakes.
predicate = option.ToFilterFunc<Foo>(StringComparison.OrdinalIgnoreCase);
expected = _foos.Where(predicate);
Assert.Equal(4, expected.Count());

option.Searches.Clear();
option.Searches.Add(new SearchFilterAction("Name", "Mock"));
predicate = option.ToFilterFunc<Foo>();
Expand Down
Loading