0

我在我们的一个项目中发现了下面的一段代码。我被困了两天:-(试图理解 Aggregate & LinQKit Expand。

您可以帮助将 LINQ 以下转换为正常的 foreach 操作吗?

public Expression<Func<Purchase, bool>> forTarget(List<string> idList)
{
    Expression<Func<Purchase, string>> fc = p => p.ClientId;
    Expression<Func<Purchase, bool>> predicate = m => false;

    return idList.Aggregate(predicate, (p, id) => p.Or(m => fc.Invoke(m) == id), p => p.Expand());
}

internal class Purchase
{
    public int Price { get; set; }
    public string Description { get; set; }
    public string ClientId { get; set; }
}

public class Client
{
    public string Id { get; set; }
}   

或者至少,任何关于这个 LINQ 表达式在列表中所做的事情的指针都会非常有帮助。

return idList.Aggregate(predicate,
        (p, id) => p.Or(m => fc.Invoke(m) == id),
        p => p.Expand());
4

1 回答 1

2

该函数遍历项目集合并通过为每个属性值添加or条件来构建谓词。ClientId

在 Linq2SQL 的早期版本中,不支持方法Contains,因此您无法执行如下查询:

IEnumerable<Purchase> purchases = LoadSelectedItems();
var clientIds = purchases.Select( p => p.ClientId ).ToArray();
var results = db.Clients.Where( c => clientIds.Contains( c.Id )); // Did not work.

此问题的解决方法是创建一个谓词,该谓词将使用oranId是否匹配特定值来检查。所以,对于上面的例子,如果clientIds = {1, 2, 3}子句Where写成:

var results = db.Clients.Where( c => c.Id == 1 || c.Id == 2 || c.Id == 3);

如您所见,这种语句不是很优雅,当要检查的值集合(即clientIds)非常大时变得不可读,最重要的是,您无法先验地知道硬编码它们的值是什么。因此,为了克服这个问题,解决方案是用一个可变的值集合来概括上述谓词。这可以通过以下算法简单地完成:

  1. 创建一个Expression返回的false;如果我们返回true,编译器将短路评估(因为我们正在使用or),并将为所有项目返回 true;
  2. 为值集合中的每个项目添加一个or包含项目值的子句。

现在,您的示例可以通过这种方式转换为 foreach:

// start with a predicate returning false
// this is the seed of the Aggregate method
Expression<Func<Purchase, bool>> predicate = m => false;
// Now, iterate the collection and build the full predicate
foreach( var id in idList)
{
    // Build the predicate by invoking a function which returns the client id of the 
    // purchase and comparing it with the value of the current id from the idList
    predicate = predicate.Or(item => item.ClientId == id);
}

希望这可以帮助。

于 2014-10-21T07:06:32.680 回答