在LINQ to Entities Expression中调用除String之外的类型的Contains()方法

时间:2013-05-09 10:52:52

标签: c# lambda expression

我正在尝试在网格中实现搜索/过滤,允许用户创建涉及列,操作和值的过滤条件。

示例: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;

1 个答案:

答案 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);