Преобразуйте LINQ в обычный Foreach
Я нашел ниже кусок кода в одном из наших проектов. Я застрял на два дня:-(пытаюсь понять 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());
1 ответ
Функция перебирает коллекцию элементов и создает предикат, добавляя 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.
Обходной путь для этой проблемы должен создать предикат, который проверял бы, используя or
будь то Id
будет соответствовать конкретному значению. Итак, для приведенного выше примера, если clientIds = {1, 2, 3}
Where
пункт будет написан как:
var results = db.Clients.Where( c => c.Id == 1 || c.Id == 2 || c.Id == 3);
Как вы можете видеть, такого рода утверждение не очень элегантно, становится нечитаемым, когда набор значений для проверки (т.е. clientIds
) очень велика, и, самое главное, вы не можете знать априори, какие будут значения для их жесткого кодирования. Таким образом, чтобы преодолеть эту проблему, решение состоит в том, чтобы обобщить вышеупомянутый предикат с переменной коллекцией значений. И это делается просто с помощью следующего алгоритма:
- Создать
Expression
это возвращаетfalse
; если мы вернемсяtrue
компилятор закоротит оценку (потому что мы используемor
) и вернет true для всех элементов; - Для каждого элемента в коллекции значений добавьте
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);
}
Надеюсь это поможет.