Условные включают в linq для объектов?
Мне казалось, что следующее должно быть возможно. Я просто не уверен, какой подход принять.
То, что я хотел бы сделать, это использовать метод include для формирования моих результатов, т.е. определить, как далеко по объективному графу перемещаться. но... Я бы хотел, чтобы этот обход был условным.
something like...
dealerships
.include( d => d.parts.where(p => p.price < 100.00))
.include( d => d.parts.suppliers.where(s => s.country == "brazil"));
Я понимаю, что это неправда linq, на самом деле, что это ужасно неправильно, но по существу я ищу способ построить дерево выражений, которое вернет форменные результаты, эквивалентные...
select *
from dealerships as d
outer join parts as p on d.dealerid = p.dealerid
and p.price < 100.00
outer join suppliers as s on p.partid = s.partid
and s.country = 'brazil'
с акцентом на условия соединения.
Я чувствую, что это будет довольно просто с esql, но мое предпочтение будет заключаться в том, чтобы строить деревья выражений на лету.
как всегда, благодарен за любые советы или рекомендации
Ответы
Ответ 1
Это должно сделать трюк:
using (TestEntities db = new TestEntities())
{
var query = from d in db.Dealership
select new
{
Dealer = d,
Parts = d.Part.Where
(
p => p.Price < 100.0
&& p.Supplier.Country == "Brazil"
),
Suppliers = d.Part.Select(p => p.Supplier)
};
var dealers = query.ToArray().Select(o => o.Dealer);
foreach (var dealer in dealers)
{
Console.WriteLine(dealer.Name);
foreach (var part in dealer.Part)
{
Console.WriteLine(" " + part.PartId + ", " + part.Price);
Console.WriteLine
(
" "
+ part.Supplier.Name
+ ", "
+ part.Supplier.Country
);
}
}
}
Этот код предоставит вам список дилеров, каждый из которых содержит отфильтрованный список деталей. Каждая часть ссылается на Поставщика. Интересная часть состоит в том, что вам нужно создать анонимные типы в выбранном порядке. В противном случае свойство Part объектов Dealership будет пустым.
Кроме того, вы должны выполнить инструкцию SQL, прежде чем выбирать дилеров из запроса. В противном случае свойство Части дилеров снова будет пустым. Поэтому я помещаю вызов ToArray() в следующую строку:
var dealers = query.ToArray().Select(o => o.Dealer);
Но я согласен с Дарреном, что это может быть не то, что ожидают пользователи вашей библиотеки.
Ответ 2
Вы уверены, что это то, что вы хотите? Единственная причина, по которой я спрашиваю, когда вы добавляете фильтр на Parts off of Dealerships, ваши результаты больше не являются дилерскими центрами. Вы имеете дело с особыми объектами, которые по большей части очень близки к Dealerships (с теми же свойствами), но значение свойства "Parts" отличается. Вместо того, чтобы быть отношениями между Дилерами и частями, это отфильтрованные отношения.
Или, говоря иначе, если я вытащил дилерство из ваших результатов и передал метод, который я написал, а затем в своем методе я вызываю:
var count = dealership.Parts.Count();
Я ожидаю получить детали, а не отфильтрованные части из Бразилии, где цена меньше 100 долларов.
Если вы не используете объект дилерства для передачи отфильтрованных данных, это становится очень простым. Он становится таким простым, как:
var query = from d in dealerships
select new { DealershipName = d.Name,
CheapBrazilProducts = dealership.Parts.Where(d => d.parts.Any(p => p.price < 100.00) || d.parts.suppliers.Any(s => s.country == "brazil")) };
Если мне просто нужно было получить отфильтрованные наборы, как вы просили, я бы, вероятно, использовал технику, упомянутую выше, а затем использовал инструмент, например Automapper, для копирования отфильтрованных результатов из моего анонимного класса в настоящий класс. Это не невероятно элегантно, но оно должно работать.
Я надеюсь, что это поможет! Это была интересная проблема.
Ответ 3
Я что-то упустил, или вы просто ищете ключевое слово Any
?
var query = dealerships.Where(d => d.parts.Any(p => p.price < 100.00) ||
d.parts.suppliers.Any(s => s.country == "brazil"));
Ответ 4
Да, что я хотел сделать, я думаю, что следующая версия Data Services будет иметь возможность делать именно эти запросы LINQ to REST, которые были бы замечательными в то время, когда я просто переключился на загрузку обратного и включил связанный объект который будет загружен несколько раз, но теоретически ему просто нужно загрузить один раз в первом Include, как в этом коде
return this.Context.SearchHistories.Include("Handle")
.Where(sh => sh.SearchTerm.Contains(searchTerm) && sh.Timestamp > minDate && sh.Timestamp < maxDate);
прежде чем я попытался загрузить для любой Handle searchHistories, которая соответствовала логике, но не знаю, как использовать логику Include, которую вы разместили, поэтому в среднем я считаю, что обратный поиск будет не столь грязным решением
Ответ 5
Я знаю, что это может работать с одним Include. Никогда не тестируйте с двумя включенными, но стоит попробовать:
dealerships
.Include( d => d.parts)
.Include( d => d.parts.suppliers)
.Where(d => d.parts.All(p => p.price < 100.00) && d.parts.suppliers.All(s => s.country == "brazil"))