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

Bak*_*kri 6 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);
}
Run Code Online (Sandbox Code Playgroud)

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

"类型'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);
}
Run Code Online (Sandbox Code Playgroud)

值列表超过10000的问题,因此性能低,我得到了这个例外

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

我有任何方法来构建动态lambda表达式,生成像这个查询

select * from x where id in (select id from y)
Run Code Online (Sandbox Code Playgroud)

Lua*_*aan 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);
}
Run Code Online (Sandbox Code Playgroud)

我也会避免dynamic在 LINQ 查询中使用 - 你使用它的方式,它并不比拥有更好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;
}
Run Code Online (Sandbox Code Playgroud)

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

这可以简化获取方法信息的过程:

Method((IQueryable<T> queryable, T item) => queryable.Contains(item))
Run Code Online (Sandbox Code Playgroud)

或者(以避免生成所有可能的重载):

Method(() => default(IQueryable<T>).Contains(default(T)))
Run Code Online (Sandbox Code Playgroud)