类型 'System.Data.Linq.DataQuery`1[System.Object]' 上不存在方法 'Contains'

No method 'Contains' exists on type '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);
    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);
}

在 运行 时我遇到了这个异常。

"No method 'Contains' exists on type '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,因此性能很低,我遇到了这个异常

"The incoming request has too many parameters. The server supports a maximum of 2100 parameters. Reduce the number of parameters and resend the request."

我有什么方法可以动态构建像这个查询一样生成的 lambda 表达式

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

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

问题是 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

但是,这不会解决您遇到的 second 问题 - 它非常明确。您传递的可枚举项太多了。除非您的 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)))