c# - C# 使用动态 Linq 表达式使用 Startswith 过滤 int 类型的列并获取 Nullable`1 进行转换
问题描述
作为使用动态 linq 进行过滤的基础,我使用了这个答案,它适用于 String 类型的列,但是我还需要对整数类型的列执行startsWith/Contains - 在这种情况下,我使用了这个答案- 它与错误如下所示。
这是创建 IQueryable 结果的代码。
public static IQueryable<T> Has<T>(this IQueryable<T> source, ExpressionFilter filter) {
if (source == null || filter.PropertyName.IsNull() || filter.Value == null) {
return source;
}
// Find the type incase we get an int column.
Type propertyType = source.ElementType.GetProperty(filter.PropertyName).PropertyType;
// For our Call.
MethodCallExpression methodCallExpression = null;
// If its a string we need to change it to lower case.
MethodCallExpression toLowerExpression = null;
// If its any one of the binary expressions.
Expression binaryExpression = null;
// ..and our predicate is initiated here as well.
Expression<Func<T, bool>> predicate = null;
// We need the parameter eg x => ..
ParameterExpression parameter = Expression.Parameter(source.ElementType, "x");
// Finally here is our Property expression eg x => LastName Last name being the property name
Expression propertyExp = Expression.Property(parameter, filter.PropertyName);
// our METHODINFO's
var CONTAINS_METHOD = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var STARTS_WITH_METHOD = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var ENDS_WITH_METHOD = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var BOOL_EQUAL_METHOD = typeof(bool).GetMethod("Equals", new Type[] { typeof(bool) });
var TO_LOWER_METHOD = typeof(string).GetMethod("ToLower", new Type[] { });
var TRIM_START = typeof(string).GetMethod("Trim", new Type[] { typeof(int) });
var STRING_CONVERT = typeof(SqlFunctions).GetMethod("StringConvert", new[] { typeof(double?) }); ////get the SqlFunctions.StringConvert method for nullable double
// We supply a type of object for our term to search on.. it needs to be a string.
ConstantExpression termConstant = Expression.Constant(filter.Value.ToString(), typeof(string));
// In case we get a propertyType of (int) we cant just perform a lower case on it.
if (propertyType == typeof(string)) {
toLowerExpression = Expression.Call(propertyExp, TO_LOWER_METHOD);
}
switch (filter.Comparison) {
case Comparison.Contains:
methodCallExpression = Expression.Call(toLowerExpression, CONTAINS_METHOD, termConstant);
break;
case Comparison.StartsWith:
if (propertyType == typeof(int)) { // We'll do a startsWith on an int column
//convert Expression to a nullable double (or nullable decimal),
//so that you can use SqlFunctions.StringConvert
propertyExp = Expression.Convert(propertyExp, typeof(double?));
//call StringConvert on your converted expression
propertyExp = Expression.Call(null, STRING_CONVERT, propertyExp);
methodCallExpression = Expression.Call(propertyExp, STARTS_WITH_METHOD, termConstant);
}
else
methodCallExpression = Expression.Call(toLowerExpression, STARTS_WITH_METHOD, termConstant); //WORKS HERE..
break;
case Comparison.EndsWith:
methodCallExpression = Expression.Call(toLowerExpression, ENDS_WITH_METHOD, termConstant);
break;
case Comparison.BoolTest:
bool parsedBoolValue;
if (bool.TryParse(filter.Value.ToString().ToLower(), out parsedBoolValue)) { // Its a bool column.
termConstant = Expression.Constant(parsedBoolValue, typeof(bool));
methodCallExpression = Expression.Call(propertyExp, BOOL_EQUAL_METHOD, termConstant);
}
break;
case Comparison.Equal:
binaryExpression = Expression.Equal(propertyExp, termConstant);
break;
case Comparison.GreaterThan:
binaryExpression = Expression.GreaterThan(propertyExp, termConstant);
break;
case Comparison.GreaterThanOrEqual:
binaryExpression = Expression.GreaterThanOrEqual(propertyExp, termConstant);
break;
case Comparison.LessThan:
binaryExpression = Expression.LessThan(propertyExp, termConstant);
break;
case Comparison.LessThanOrEqual:
binaryExpression = Expression.LessThanOrEqual(propertyExp, termConstant);
break;
case Comparison.NotEqual:
binaryExpression = Expression.NotEqual(propertyExp, termConstant);
break;
case Comparison.IndexOf:
binaryExpression = Expression.NotEqual(
Expression.Call(
propertyExp,
"IndexOf",
null,
Expression.Constant(filter.Value, typeof(string)),
Expression.Constant(StringComparison.InvariantCultureIgnoreCase, typeof(StringComparison))
),
Expression.Constant(-1, typeof(int))
);
break;
default:
return null;
}
if (binaryExpression == null) {
predicate = Expression.Lambda<Func<T, bool>>(methodCallExpression, parameter);
}
else {
predicate = Expression.Lambda<Func<T, bool>>(binaryExpression, parameter);
}
methodCallExpression = Expression.Call(typeof(Queryable), "Where",
new Type[] { source.ElementType },
source.Expression, Expression.Quote(predicate));
return source.Provider.CreateQuery<T>(methodCallExpression);
}
因此,它适用于字符串类型的列,但是当您尝试对 int 列进行过滤时,它会失败。
“由于对象的当前状态,操作无效。”
对于 int 列,它使用以下方式构建表达式:
if (propertyType == typeof(int)) { // We'll do a startsWith on an int column
//convert Expression to a nullable double (or nullable decimal),
//so that you can use SqlFunctions.StringConvert
propertyExp = Expression.Convert(propertyExp, typeof(double?));
//call StringConvert on your converted expression
propertyExp = Expression.Call(null, STRING_CONVERT, propertyExp);
methodCallExpression = Expression.Call(propertyExp, STARTS_WITH_METHOD, termConstant);
}
作为一个表达式,这会产生
{x => StringConvert(Convert(x.ClientNo, Nullable`1)).StartsWith("101")}
我在这里可能是错的,但为什么它会在可为空后加上一个反勾和一个“1”?这就是它倒下的原因吗?
为了完整性,它完成了“Has”方法(添加到表达式树),这是结果(失败):
有没有更好的方法来构造这个表达式以便它工作?
解决方案
推荐阅读
- kubernetes - kubectl exec 权限被拒绝
- liferay - 在 liferay 6.2 Portal 中导入大型 .lar 文件时出现问题?
- typescript - 简化 if 语句 - Angular
- ios - 如何在swift 5中的每个控制器上调用一个函数
- mysql - SQL LEFT JOIN 用于第一个表的所有记录和另一个表的匹配记录
- azure - 配置为从死信队列接收消息时未收到常规服务总线消息
- python - 我无法在我的项目上运行 SonarQube
- python - 将布尔值与整数混合时,Mypy 不会抛出错误
- javascript - 如何获取元素的新值?- Javascript
- javascript - 如何监听 firestore 子集合中所有文档的更改?