C# 在LINQ to Entities表达式中调用Contains()方法,该表达式的类型不是字符串

C# 在LINQ to Entities表达式中调用Contains()方法,该表达式的类型不是字符串,c#,lambda,expression,C#,Lambda,Expression,我试图在网格中实现搜索/过滤,允许用户创建包含列、操作和值的过滤条件 示例:Column1包含“somevalue” 当所选列包含字符串类型时,以下代码可以正常工作: case WhereOperation.Contains: Expression condition = Expression.Call( memberAccess, //memberAccess is a MemberExpression of the property bound to the colum

我试图在网格中实现搜索/过滤,允许用户创建包含列、操作和值的过滤条件

示例: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”方法。在调用memberAccess的“Contains”之前,如何首先获取它的
ToString()

注1:“memberAccess”表示的属性类型在编译时未知。
注2:lambda表达式最终用于无法显式处理
ToString()
的LINQ 2实体查询。(参见下面我尝试的内容)

这是我尝试过的一个解决方案,但在LINQ表达式求值时失败了,因为LINQ 2实体不支持
ToString()


嗯。。。由于安全原因,不能在Linq to实体中使用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);
    }
}
公共静态类ExpressionExtensions
{
公共静态表达式AddContains(此LambdaExpression选择器,字符串值)
{
var mi=typeof(string).GetMethods().First(m=>m.Name==“包含”&&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());
返回(表达式)e;
}
公共静态表达式GetBody(此LambdaExpression表达式)
{
表达体;
if(expression.Body是UnaryExpression)
body=((UnaryExpression)expression.body).操作数;
其他的
body=表达式body;
返回体;
}
公共静态表达式关联(此表达式)
{
if(expression.Type==typeof(string))
返回表达式;
MethodInfo toString=typeof(SqlFunctions).GetMethods().First(m=>m.Name==“StringConvert”&&m.GetParameters().Length==1&&m.GetParameters()[0]。ParameterType==typeof(double?);
var cast=Expression.Convert(表达式,typeof(double?);
返回表达式.Call(toString,cast);
}
}
用法:

        IQueryable<Foo> seed = ...;
        Expression<Func<Foo, int>> selector = x => x.Id;
        var expression = selector.AddContains<Foo>("3");
        seed.Where(expression);  
IQueryable种子=。。。;
表达式选择器=x=>x.Id;
var表达式=选择器。AddContains(“3”);
种子。在哪里(表达);

尝试将其适应我的代码,以便适合我的使用,但我认为传递给AsString()的长度存在问题。它需要是选择器值的长度,而不是作为参数传递给Contains()的值。我不确定如何获得选择器值的长度。我没有放弃这个问题。我刚刚提出了一些其他的优先事项,所以我没有机会回到这段代码。我们应该在周二开始讨论,并在适当的时候接受更多的回答或评论。很抱歉耽搁了。很抱歉花了这么长时间,但我终于能够回到这个问题上来,您的解决方案非常有效。谢谢+1:@maxlego当一个人开始学习表情树的时候,所有这些东西看起来一点都不简单……非常感谢。你的回答确实很有帮助
        IQueryable<Foo> seed = ...;
        Expression<Func<Foo, int>> selector = x => x.Id;
        var expression = selector.AddContains<Foo>("3");
        seed.Where(expression);