【发布时间】:2021-11-12 11:47:57
【问题描述】:
我正在为 Blazor 创建一个 DataTable 组件。它正在工作,但是当我想使用Like 动态过滤内存中的数据时遇到问题(请参阅代码here)
public IList<TModel> Items { get; set; } = new List<TModel>();
private IQueryable<TModel> AllItems { get; set; }
if (AllFilterRules.Count == 0) Items = AllItems;
else
{
Expression<Func<TModel, bool>>? filterExpression = null;
foreach (var filterRule in AllFilterRules)
{
var filterRuleExpression = filterRule.GenerateExpression();
if (filterExpression == null) filterExpression = filterRuleExpression;
else filterExpression = PredicateBuilder.And(filterExpression, filterRuleExpression);
}
Items = AllItems.Where(filterExpression).ToList();
useFilteredResult = true;
}
暴击:Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
未处理的异常呈现组件:不支持“Like”方法,因为查询已切换到客户端评估。这通常发生在方法的参数无法转换到服务器时。重写查询以避免客户端评估参数,以便将方法转换为服务器。
System.InvalidOperationException:不支持“Like”方法,因为查询已切换到客户端评估。这通常发生在方法的参数无法转换到服务器时。重写查询以避免客户端评估参数,以便将方法转换为服务器。
在 Microsoft.EntityFrameworkCore.DbFunctionsExtensions.LikeCore(String matchExpression, String pattern, String escapeCharacter)
在 Microsoft.EntityFrameworkCore.DbFunctionsExtensions.Like(DbFunctions _, String matchExpression, String pattern)
在 System.Linq.Enumerable.WhereArrayIterator`1[[PSC.Blazor.Examples.Data.WeatherForecast, PSC.Blazor.Examples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
在 System.Collections.Generic.List
1[[PSC.Blazor.Examples.Data.WeatherForecast, PSC.Blazor.Examples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]..ctor(IEnumerable1 集合)在 System.Linq.Enumerable.ToList[WeatherForecast](IEnumerable`1 源)
在 PSC.Blazor.Components.DataTable.DataTable`1[[PSC.Blazor.Examples.Data.WeatherForecast, PSC.Blazor.Examples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].PerformClientSideDataManipulations () 在 C:\Projects\PSC.Blazor.Components.DataTable\PSC.Blazor.Components.DataTable\DataTable.razor:560 行
在 PSC.Blazor.Components.DataTable.DataTable`1.d__172[[PSC.Blazor.Examples.Data.WeatherForecast,PSC.Blazor.Examples,版本=1.0.0.0,文化=中性,PublicKeyToken=null]] C:\Projects\PSC.Blazor.Components.DataTable\PSC.Blazor.Components.DataTable\DataTable.razor 中的 .MoveNext():第 511 行
在 Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(任务任务)
在 Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle)
创建表达式过滤器的函数如下
private class ContainsFilter : ObjectFilter
{
public override bool ValueRequired => true;
public override bool IsNumberAllowed => false;
public override bool IsBoolAllowed => false;
public override bool IsStringAllowed => true;
public override bool IsDateTimeAllowed => false;
public override bool IsNonNullableAllowed => true;
internal ContainsFilter(int id, string name)
: base(id, name)
{
}
public override Expression<Func<TModel, bool>> GenerateExpression<TModel>(
string propertyName,
object value)
{
Expression expression = (Expression)Expression.Parameter(typeof(TModel), "e");
string str = propertyName;
char[] chArray = new char[1] { '.' };
foreach (string propertyOrFieldName in str.Split(chArray))
expression = (Expression)Expression.PropertyOrField(expression, propertyOrFieldName);
ConstantExpression constantExpression = Expression.Constant((object)string.Format("%{0}%", value));
return Expression.Lambda<Func<TModel, bool>>(Expression.Call(typeof(DbFunctionsExtensions),
nameof(DbFunctionsExtensions.Like), null, Expression.Constant(EF.Functions),
expression, constantExpression));
}
}
但我认为这个功能运行良好。问题是当我将过滤器应用于AllItems。
Items = AllItems.Where(filterExpression).ToList();
另外,我试图用
改变这一行Items = new List<TModel>();
var t = AllItems.Where(filterExpression);
foreach (var i in t)
Items.Add(i);
但我得到了相同的结果。
更新
这是我看到的filterExpression 变量的快速观察。
我有另一个Equals 的功能,它正在工作
private class IsEqualsFilter : ObjectFilter
{
public override bool ValueRequired => true;
public override bool IsNumberAllowed => true;
public override bool IsBoolAllowed => true;
public override bool IsStringAllowed => true;
public override bool IsDateTimeAllowed => true;
public override bool IsNonNullableAllowed => true;
internal IsEqualsFilter(int id, string name)
: base(id, name)
{
}
public override Expression<Func<TModel, bool>> GenerateExpression<TModel>(
string propertyName,
object value)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(TModel), "e");
Expression expression = (Expression)parameterExpression;
string str = propertyName;
char[] chArray = new char[1] { '.' };
foreach (string propertyOrFieldName in str.Split(chArray))
expression = (Expression)Expression.PropertyOrField(expression, propertyOrFieldName);
UnaryExpression unaryExpression = !expression.Type.IsEnum ?
Expression.ConvertChecked(Expression.Constant(value), expression.Type) :
Expression.ConvertChecked(Expression.Constant(
(object)Convert.ToInt32(Enum.Parse(expression.Type, value.ToString()))),
expression.Type);
return Expression.Lambda<Func<TModel, bool>>(Expression.Equal(expression, unaryExpression),
parameterExpression);
}
}
更新/2
正如Richard 所说,在 EF Core 3.1 中,DbFunctionExtensions.Like 方法适用于内存查询;但此后代码已更新为仅在 commit 中引发 InvalidOperationException。 Expression<Func<TModel, bool>> 的通用实现适用于除 string 之外的各种 Type。
所以,我为string添加了一个自定义实现
public override IList<TModel> ApplyEmbeddedFilter<TModel>(IList<TModel> models,
string propertyName, string value)
{
var searchStrLower = value.ToLower();
var propsToCheck = typeof(TModel).GetProperties()
.Where(a => a.PropertyType == typeof(string) &&
a.Name == propertyName && a.CanRead);
return models.Where(obj => {
foreach (PropertyInfo prop in propsToCheck)
{
string value = (string)prop.GetValue(obj);
if (value != null && value.ToLower().Contains(searchStrLower))
return true;
}
return false;
}).ToList();
}
【问题讨论】:
-
异常信息怎么样,是没用还是不清楚?
-
尝试检查 filterExpression 的
DebugView:它是否包含内联的filterExpression和filterRuleExpression部分,或者它是否尝试调用另一个 lambda? -
这看起来像原始代码,而不是来自yesterday's question 的固定代码。
-
谢谢大家的回答。 @RichardDeeming 没有。正如你所建议的,我将
AllItems更改为IQuerable,但我得到了同样的错误。 -
令人讨厌的是,在 EF Core 3.1 中,
DbFunctionExtensions.Like方法适用于内存查询;但此后代码已更新为仅在 commit 47d1ebedf834f1b4caae4d4f45f5f54d788e31af 中抛出InvalidOperationException。
标签: c# linq expression