我正在尝试在网格中实现搜索/过滤,允许用户创建涉及列,操作和值的过滤条件。
示例:Column1包含“somevalue”
当所选列包含字符串类型时,以下代码可以正常工作:
case WhereOperation.Contains:
Expression condition = Expression.Call(
memberAccess, //memberAccess is a MemberExpression of the property bound to the column.
typeof(string).GetMethod("Contains"),
Expression.Constant(value.ToString())); // value is what we are checking to see if the column contains.
LambdaExpression lambda = Expression.Lambda(condition, parameter);
break;
但是,当列绑定的属性不是string类型(即Int)时,由于类型Int
没有“Contains”方法,因此失败。如何在调用“Contains”之前首先获取memberAccess的ToString()
值?
注1:“memberAccess”表示的属性类型在编译时是未知的
注意2:lambda表达式最终用于LINQ 2实体查询,该查询无法显式处理ToString()
。 (看看我在下面尝试过的)
这是我尝试过的一个解决方案,但在LINQ表达式评估时它失败了,因为LINQ 2实体不支持ToString()
:
case WhereOperation.Contains:
Expression stringValue = Expression.Call(memberAccess,
typeof(object).GetMethod("ToString"));
Expression condition = Expression.Call(
stringValue,
typeof(string).GetMethod("Contains"),
Expression.Constant(value.ToString()));
LambdaExpression lambda = Expression.Lambda(condition, parameter);
break;
答案 0 :(得分:7)
是的......出于安全原因,你无法在Linq to Entities中使用ToString。不记得我在哪里读它。它虽然在Linq2Sql中支持。 但是你可以使用SqlFunctions.StringConvert。
我为你做了简单的扩展:
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> AddContains<T>(this LambdaExpression selector, string value)
{
var mi = typeof (string).GetMethods().First(m => m.Name == "Contains" && m.GetParameters().Length == 1);
var body = selector.GetBody().AsString();
var x = Expression.Call(body, mi, Expression.Constant(value));
LambdaExpression e = Expression.Lambda(x, selector.Parameters.ToArray());
return (Expression<Func<T, bool>>)e;
}
public static Expression GetBody(this LambdaExpression expression)
{
Expression body;
if (expression.Body is UnaryExpression)
body = ((UnaryExpression)expression.Body).Operand;
else
body = expression.Body;
return body;
}
public static Expression AsString(this Expression expression)
{
if (expression.Type == typeof (string))
return expression;
MethodInfo toString = typeof(SqlFunctions).GetMethods().First(m => m.Name == "StringConvert" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(double?));
var cast = Expression.Convert(expression, typeof(double?));
return Expression.Call(toString, cast);
}
}
用法:
IQueryable<Foo> seed = ...;
Expression<Func<Foo, int>> selector = x => x.Id;
var expression = selector.AddContains<Foo>("3");
seed.Where(expression);