类型'System.Data.Linq.DataQuery`1 [System.Object]'上没有方法'Contains'

时间:2015-07-02 06:32:07

标签: c# linq dynamic

我正在尝试构建包含表达式。

private Expression<Func<T, bool>> Contains<T>(string property, IEnumerable<dynamic> values, T item)
{
    ParameterExpression pe = Expression.Parameter(item.GetType(), "c");
    Expression columnNameProperty = Expression.Property(pe, property);
    var someValueContain = Expression.Constant(values, values.GetType());
    var convertExpression = Expression.Convert(columnNameProperty, typeof(Guid));
    Expression expression = Expression.Call(someValueContain, "Contains", new Type[] { }, convertExpression);

    return Expression.Lambda<Func<T, bool>>(expression, pe);
}

在运行时我得到了这个例外。

  

“类型上没有方法'包含'   'System.Data.Linq.DataQuery`1 [System.Object的]'“。

灵魂就是将值参数转换为列表

private Expression<Func<T, bool>> Contains<T>(string property, IEnumerable<dynamic> values, T item)
{
    ParameterExpression pe = Expression.Parameter(item.GetType(), "c");
    Expression columnNameProperty = Expression.Property(pe, property);
    Guidvalues = values.Cast<Guid>().ToList();
    var someValueContain = Expression.Constant(Guidvalues, Guidvalues.GetType());
    var convertExpression = Expression.Convert(columnNameProperty, typeof(Guid));
    Expression expression = Expression.Call(someValueContain, "Contains", new Type[] { }, convertExpression);

    return Expression.Lambda<Func<T, bool>>(expression, pe);
}

值列表超过10000的问题因此性能很低而且我得到了这个异常

  

“传入的请求参数太多。服务器支持a   最多2100个参数。减少参数数量并重新发送   请求。“

我有任何方法可以构建动态lambda表达式,生成类似于此查询的

select * from x where id in (select id from y)

1 个答案:

答案 0 :(得分:3)

这只是语法糖让你变得更好:)

问题是Contains确实不是DataQuery<T>上的方法 - 它是System.Linq.Queryable中的静态方法。 C#编译器通过扩展方法为您处理这个问题,但这只是C#编译器 - 它不是IL的一个特性,它是C#的一个特性。因此,当您操纵表达树或发出原始IL时,您必须自己处理它:

private Expression<Func<T, bool>> Contains<T, V>
 (string property, IQueryable<V> values, T item)
{
        ParameterExpression pe = Expression.Parameter(item.GetType(), "c");
        Expression columnNameProperty = Expression.Property(pe, property);
        var someValueContain = Expression.Constant(values, values.GetType());
        var convertExpression = Expression.Convert(columnNameProperty, typeof(V));
        Expression expression = 
          Expression.Call
          (
            (
             ((Expression<Func<bool>>)
             (() => Queryable.Contains(default(IQueryable<V>), default(V)))
            )
            .Body as MethodCallExpression).Method, 
            someValueContain, 
            convertExpression
          );

        return Expression.Lambda<Func<T, bool>>(expression, pe);
}

我也避免在LINQ查询中使用dynamic - 你使用它的方式,它并不比拥有IEnumerable<object>更好,如果你希望它始终是Guid无论如何,只是让它通用:)

此方法假定列的任何类型都可以转换为您传递的可枚举项的类型,当然,它适用于任何类型,而不仅仅是Guid

但是,这不会解决您的第二个问题 - 它非常明确。你传递的可枚举的物品太多了。除非您的LINQ提供程序有更好的传递值的方法,否则您将不得不求助于将查询拆分为多个单独的查询,每个查询都用于例如1000个项目,然后将结果重新加入。除非当然,我的猜测是正确的,你传递的values实际上也是一个可查询的 - 在这种情况下,代码应该可以正常工作。

修改

我找到了获得正确MethodInfo的最佳方法是一组这样的方法:

public static MethodInfo Method<TR>(Expression<Func<TR>> expression)
{
    return (expression.Body as MethodCallExpression).Method;
}

public static MethodInfo Method<T1, TR>(Expression<Func<T1, TR>> expression)
{
    return (expression.Body as MethodCallExpression).Method;
}

(自动生成的方式与实际的Func<...>代表相同)

这样可以简化方法信息:

Method((IQueryable<T> queryable, T item) => queryable.Contains(item))

或者(为了避免产生所有可能的重载):

Method(() => default(IQueryable<T>).Contains(default(T)))