0

我需要一种方法来检查我Queryable是否有任何谓词Where

var queryA = db.Contacts.Where(x => x.IsAdministrator);

var hasPredicate = HasPredicate(queryA);
// return true

var queryB = db.Contacts;

var hasPredicateB = HasPredicate(queryB);
// return false

我知道这不是最好的方法,显然你们都同意。

我将快速而肤浅地解释一下:

我有一个返回查询的方法,该查询将调用我在另一个上下文中用作主要研究的 QueryBase。

在当前上下文中,我需要 QueryBase ID 列表来进行连接。

不管怎样,我们开始吧,这些研究中使用的对象非常庞大和复杂,所以不想“重做”QueryBase。当这个巨大的对象没有标准时,查询是“原始的”。

我知道,我应该验证对象是否为空,但这确实有效,因为有几个不同的默认值,它们根据其他值而有所不同,并且所有这些验证都发生在返回 QueryBase 的方法中。

如果查询是“空的”,我只需要跳过这个。

4

2 回答 2

4

可以使用该IQueryable.Expression属性,并查看它是否包括对Queryable.Where. 但是,总的来说,这感觉是一种非常脆弱的方法,您几乎可以肯定地重新考虑您的设计以避免需要这些知识。

于 2013-07-30T21:11:14.017 回答
2

Linq 的 IQueryable 端在表达式上运行。表达式是一种基于对象的树结构,或多或少代表了您可以对 Queryable 执行的所有操作。这允许“代码即数据”,允许代码动态构建其他代码,然后可以将其评估为构建其他东西(如 SQL)的结构,或者编译并运行以在运行时内产生结果。

在您的特定情况下,您将遍历此表达式树,从上到下,从左到右,寻找类型的节点MethodCallExpression(或具有 NodeType 的ExpressionType.Call),引用 MethodInfo 描述名为“System.Linq.Queryable.Where `2"。

现在,正如 Jon Skeet 和其他人所说,这很脆弱。首先,您必须实现一个 Expression walker,它将正确遍历所有其他类型的 Expression 节点,以确保您遍历整个树以找到您的 Where 子句。其次,可能有一个不在“主干”IQueryable 上运行的 Where 方法;例如,它可能在子集合上运行的 lambda 语句中:

db.Contacts
    .OrderBy(c=>c.PhoneNumbers
                    .Where(pn=>pn.Type == PhoneType.Mobile)
                    .OrderBy(pn=>pn.DateLastCalledSuccessfully)
                    .FirstOrDefault());

这是另一个可能的陷阱。FirstOrDefault()(and LastOrDefault()) 方法具有接受谓词的重载。它们不一定会评估为对 Queryable.Where 函数的调用。出于同样的原因,最终用户可以定义自己的谓词样式方法(FilterBy()WhereNot()等),以供自己使用或匹配他打算在其应用程序中向最终用户显示的查询语法。您的代码,如果它只查找System.Linq.Queryable.Where()(或者即使它查找任何名为“Where”的方法),也不会找到这些。出于同样的原因,您搜索的范围越广,您找到的方法就越多,这些方法与您期望用于谓词的名称相匹配,但它们不是基于谓词的函数。

于 2013-07-30T21:32:46.680 回答