用 Entity Framework 和 Like 进行动态表达
Make dynamic expression with Entity Framework and Like
在我的项目中,我在 运行 时创建基于 Entity Framework Core
的表达式。该代码是 Blazor 组件的一部分。我有一些基于变量类型的函数。例如,我有这个功能,它正在工作,检查一个字段是否等于一个值
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);
}
}
现在,我有另一个函数来检查字段是否包含值,但是这个函数给我一个错误
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));
}
}
我得到的错误是
System.ArgumentException: Incorrect number of parameters supplied for lambda declaration
at System.Linq.Expressions.Expression.ValidateLambdaArgs(Type
delegateType, Expression& body, ReadOnlyCollection`1 parameters, String paramName)
at System.Linq.Expressions.Expression.Lambda[Func2](Expression body, String name, Boolean tailCall, IEnumerable
1 parameters)
at System.Linq.Expressions.Expression.Lambda[Func2](Expression body, Boolean tailCall, IEnumerable
1 parameters)
at System.Linq.Expressions.Expression.Lambda[Func`2](Expression body,
ParameterExpression[] parameters)
at PSC.Blazor.Components.DataTable.Code.Enumerations.ObjectFilter.ContainsFilter.GenerateExpression[WeatherForecast](String
propertyName, Object value) in
C:\Projects\PSC.Blazor.Components.DataTable\PSC.Blazor.Components.DataTable\Code\Enumerations\ObjectFilter.cs:line
347
我遵循函数nExpression.Lambda<Func<TModel, bool>>
的签名。如何使用 Like
更改呼叫?
更新
我尝试了 Richard 建议的代码,但我遇到了这个错误。
Unhandled exception rendering component: The 'Like' method is not
supported because the query has switched to client-evaluation. This
usually happens when the arguments to the method cannot be translated
to server. Rewrite the query to avoid client evaluation of arguments
so that method can be translated to server.
System.InvalidOperationException: The 'Like' method is not supported
because the query has switched to client-evaluation. This usually
happens when the arguments to the method cannot be translated to
server. Rewrite the query to avoid client evaluation of arguments so
that method can be translated to server. at
Microsoft.EntityFrameworkCore.DbFunctionsExtensions.LikeCore(String
matchExpression, String pattern, String escapeCharacter) at
Microsoft.EntityFrameworkCore.DbFunctionsExtensions.Like(DbFunctions
_, String matchExpression, String pattern) at System.Linq.Enumerable.WhereArrayIterator1[[PSC.Blazor.Examples.Data.WeatherForecast, PSC.Blazor.Examples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() at System.Collections.Generic.List
1[[PSC.Blazor.Examples.Data.WeatherForecast,
PSC.Blazor.Examples, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]]..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[WeatherForecast](IEnumerable
1 source)
at
PSC.Blazor.Components.DataTable.DataTable`1[[PSC.Blazor.Examples.Data.WeatherForecast,
PSC.Blazor.Examples, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]].PerformClientSideDataManipulations() in
C:\Projects\PSC.Blazor.Components.DataTable\PSC.Blazor.Components.DataTable\DataTable.razor:line
559
您忘记在 Expression.Lambda
调用中指定参数:
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);
ConstantExpression valueExpression = Expression.Constant((object)string.Format("%{0}%", value));
Expression body = Expression.Call(typeof(DbFunctionsExtensions),
nameof(DbFunctionsExtensions.Like), null, Expression.Constant(EF.Functions),
expression, valueExpression);
return Expression.Lambda<Func<TModel, bool>>(body, parameterExpression);
编辑:
我怀疑 Expression.Constant(EF.Functions)
无法翻译成 SQL。幸运的是,这不是必需的;请改用 Expression.Default(typeof(DbFunctions))
。
Expression body = Expression.Call(typeof(DbFunctionsExtensions),
nameof(DbFunctionsExtensions.Like), null, Expression.Default(typeof(DbFunctions)),
expression, valueExpression);
编辑 2:
尝试明确获取 MethodInfo
:
MethodInfo likeMethod = typeof(DbFunctionsExtensions).GetMethod(
nameof(DbFunctionsExtensions.Like),
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(DbFunctions), typeof(string), typeof(string) },
null);
Expression body = Expression.Call(null,
likeMethod, Expression.Default(typeof(DbFunctions)),
expression, valueExpression);
在我的项目中,我在 运行 时创建基于 Entity Framework Core
的表达式。该代码是 Blazor 组件的一部分。我有一些基于变量类型的函数。例如,我有这个功能,它正在工作,检查一个字段是否等于一个值
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);
}
}
现在,我有另一个函数来检查字段是否包含值,但是这个函数给我一个错误
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));
}
}
我得到的错误是
System.ArgumentException: Incorrect number of parameters supplied for lambda declaration
at System.Linq.Expressions.Expression.ValidateLambdaArgs(Type delegateType, Expression& body, ReadOnlyCollection`1 parameters, String paramName)
at System.Linq.Expressions.Expression.Lambda[Func
2](Expression body, String name, Boolean tailCall, IEnumerable
1 parameters)at System.Linq.Expressions.Expression.Lambda[Func
2](Expression body, Boolean tailCall, IEnumerable
1 parameters)at System.Linq.Expressions.Expression.Lambda[Func`2](Expression body, ParameterExpression[] parameters)
at PSC.Blazor.Components.DataTable.Code.Enumerations.ObjectFilter.ContainsFilter.GenerateExpression[WeatherForecast](String propertyName, Object value) in C:\Projects\PSC.Blazor.Components.DataTable\PSC.Blazor.Components.DataTable\Code\Enumerations\ObjectFilter.cs:line 347
我遵循函数nExpression.Lambda<Func<TModel, bool>>
的签名。如何使用 Like
更改呼叫?
更新
我尝试了 Richard 建议的代码,但我遇到了这个错误。
Unhandled exception rendering component: The 'Like' method is not supported because the query has switched to client-evaluation. This usually happens when the arguments to the method cannot be translated to server. Rewrite the query to avoid client evaluation of arguments so that method can be translated to server. System.InvalidOperationException: The 'Like' method is not supported because the query has switched to client-evaluation. This usually happens when the arguments to the method cannot be translated to server. Rewrite the query to avoid client evaluation of arguments so that method can be translated to server. at Microsoft.EntityFrameworkCore.DbFunctionsExtensions.LikeCore(String matchExpression, String pattern, String escapeCharacter) at Microsoft.EntityFrameworkCore.DbFunctionsExtensions.Like(DbFunctions _, String matchExpression, String pattern) at System.Linq.Enumerable.WhereArrayIterator
1[[PSC.Blazor.Examples.Data.WeatherForecast, PSC.Blazor.Examples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() at System.Collections.Generic.List
1[[PSC.Blazor.Examples.Data.WeatherForecast, PSC.Blazor.Examples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[WeatherForecast](IEnumerable
1 source)
at PSC.Blazor.Components.DataTable.DataTable`1[[PSC.Blazor.Examples.Data.WeatherForecast, PSC.Blazor.Examples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].PerformClientSideDataManipulations() in C:\Projects\PSC.Blazor.Components.DataTable\PSC.Blazor.Components.DataTable\DataTable.razor:line 559
您忘记在 Expression.Lambda
调用中指定参数:
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);
ConstantExpression valueExpression = Expression.Constant((object)string.Format("%{0}%", value));
Expression body = Expression.Call(typeof(DbFunctionsExtensions),
nameof(DbFunctionsExtensions.Like), null, Expression.Constant(EF.Functions),
expression, valueExpression);
return Expression.Lambda<Func<TModel, bool>>(body, parameterExpression);
编辑:
我怀疑 Expression.Constant(EF.Functions)
无法翻译成 SQL。幸运的是,这不是必需的;请改用 Expression.Default(typeof(DbFunctions))
。
Expression body = Expression.Call(typeof(DbFunctionsExtensions),
nameof(DbFunctionsExtensions.Like), null, Expression.Default(typeof(DbFunctions)),
expression, valueExpression);
编辑 2:
尝试明确获取 MethodInfo
:
MethodInfo likeMethod = typeof(DbFunctionsExtensions).GetMethod(
nameof(DbFunctionsExtensions.Like),
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(DbFunctions), typeof(string), typeof(string) },
null);
Expression body = Expression.Call(null,
likeMethod, Expression.Default(typeof(DbFunctions)),
expression, valueExpression);