【问题标题】:Call Contains() method in LINQ to Entities Expression on a type other than String在 LINQ to Entities Expression 中对 String 以外的类型调用 Contains() 方法
【发布时间】:2013-05-09 10:52:51
【问题描述】:

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

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

但是,当列绑定的属性不是字符串类型(即 Int)时,这会失败,因为类型 Int 没有“包含”方法。在调用“包含”之前,如何首先获取 memberAccess 的 ToString() 值?

注1:“memberAccess”所代表的属性类型在编译时是未知的。
注意 2:lambda 表达式最终被用于无法显式处理 ToString() 的 LINQ 2 实体查询中。 (看看我在下面的尝试)

这是我尝试过的一种解决方案,但在 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;

【问题讨论】:

    标签: c# lambda expression


    【解决方案1】:

    是的...由于安全原因,您不能在 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);  
    

    【讨论】:

    • 尝试将其调整为适合我的代码,以便适合我的使用,但我认为传递给 AsString() 的长度存在问题。它需要是选择器值的长度,而不是我们作为参数传递给 Contains() 的值。我不确定如何获取选择器值的长度。
    • 我没有放弃这个问题。我刚刚提出了一些其他优先事项,所以我没有机会返回这段代码。应该在星期二得到它,并将酌情接受答案或评论。抱歉耽搁了。
    • 对不起,花了这么长时间,但我终于能够回到这个问题,你的解决方案效果很好。谢谢!
    • +1:@maxlego 当一个人开始学习表达式树时,所有这些东西看起来一点都不简单......非常感谢。你的回答确实很有帮助
    猜你喜欢
    • 2017-03-31
    • 1970-01-01
    • 2012-04-29
    • 1970-01-01
    • 2013-09-09
    • 2012-11-10
    • 2020-05-16
    • 1970-01-01
    • 2011-02-10
    相关资源
    最近更新 更多