условно включить в 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 это будет довольно просто, но я предпочитаю строить деревья выражений на лету.

как всегда, благодарен за любой совет или руководство


person tim    schedule 06.07.2009    source источник
comment
Вы когда-нибудь находили решение этой проблемы? Я с той же проблемой.   -  person Jakob Christensen    schedule 20.07.2009
comment
Еще нет, но я по-прежнему заинтересован и продолжу заниматься ...   -  person tim    schedule 21.07.2009
comment
Я искал то же самое   -  person Josue Martinez    schedule 21.05.2017


Ответы (5)


Это должно помочь:

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 объектов дилерского центра будет пустым.

Кроме того, вы должны выполнить оператор SQL перед выбором дилеров из запроса. В противном случае свойство Part дилеров снова будет пустым. Вот почему я помещаю вызов ToArray () в следующую строку:

var dealers = query.ToArray().Select(o => o.Dealer);

Но я согласен с Дарреном в том, что это может быть не то, чего ожидают пользователи вашей библиотеки.

person Jakob Christensen    schedule 22.07.2009
comment
Примечание по этому поводу, я пробовал это, и сначала это не сработало. В конце концов я обнаружил, что у меня была включена отложенная загрузка, из-за которой это не работало. Как только я отключил ленивую загрузку, это сработало отлично. Спасибо, Якоб! - person msmucker0527; 18.10.2011
comment
Это требует, чтобы вы вернули анонимный тип вместо необработанного типа с фактическими ссылками. Это действительно работает, но мне нужно решение для возврата необработанного объекта с фактическим объектом коллекции, отфильтрованным до того, что я действительно хотел вернуть. Тип анонимного возврата работает, но на самом деле это не совсем желаемое решение. - person VulgarBinary; 25.10.2012
comment
@VulgarBinary вы можете вернуть напечатанный список следующим образом: var dealers = query.ToArray().Select(o => o.Dealer).ToList(); Это просто работает! - person Akira Yamamoto; 24.09.2014
comment
Я предпочитаю этот способ, поэтому не создаю лишний массив: query.Load(); var dealers = query.Select(o => o.Dealer).ToList(); - person Akira Yamamoto; 24.09.2014

Вы уверены, что это то, что вам нужно? Единственная причина, по которой я спрашиваю, заключается в том, что после того, как вы добавите фильтр для частей вне дилерских центров, ваши результаты больше не будут дилерскими центрами. Вы имеете дело со специальными объектами, которые по большей части находятся очень близко к дилерским центрам (с такими же свойствами), но значение свойства «Детали» другое. Это не отношения между дилерскими центрами и запчастями, а отфильтрованные отношения.

Или, другими словами, если я выберу дилерский центр из ваших результатов и перейду к написанному мной методу, а затем в своем методе я вызову:

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, чтобы скопировать отфильтрованные результаты из моего анонимного класса в настоящий класс. Это не очень элегантно, но должно работать.

Надеюсь, это поможет! Это была интересная проблема.

person Darren    schedule 21.07.2009
comment
Спасибо, Даррен. Я согласен с идентичностью объекта! Я также обеспокоен тем, что именно поэтому это может не поддерживаться EF. На самом деле я пытаюсь разобрать строку запроса, такую ​​как carpartsdb.com/dealerhips/parts ( price ‹100) / suppliers (s.country == brazil) в выражение запроса и проверьте его в EF, вставьте пользовательскую стратегию сериализации xml и получите RestQL Engine (спокойный язык запросов). I Я не слишком привязан к EF, но хочу иметь абстракцию модели и деревья выражений, а EF - и не хочу отклоняться слишком далеко от стека MS! - person tim; 22.07.2009
comment
извините, Даррен, см. этот текст в дальнейшем ответе, где он будет более читабельным! - person tim; 22.07.2009

Я что-то упустил, или вы просто ищете ключевое слово Any?

var query = dealerships.Where(d => d.parts.Any(p => p.price < 100.00) || 
                              d.parts.suppliers.Any(s => s.country == "brazil"));
person lc.    schedule 06.07.2009
comment
Я так не думаю, насколько я понимаю, это приведет к возврату дилерских центров, где были детали и поставщики, которые отвечали условиям, вместо того, чтобы возвращать все дилерские центры, все детали, которые меньше 100, и всех поставщиков в Бразилии. Я понимаю, что это должно быть внешнее соединение. Я думаю, что я пытаюсь обрезать дерево результатов, а не фильтровать узлы верхнего уровня на основе значений вниз по дереву. - person tim; 06.07.2009

Да, это то, что я хотел сделать, я думаю, что в следующем выпуске Data Services будет возможность выполнять только те запросы LINQ to REST, которые были бы отличными, в то время как я просто переключился на загрузку обратного и включил связанный объект, который будет загружается несколько раз, но теоретически он должен загружаться только один раз при первом включении, как в этом коде

return this.Context.SearchHistories.Include("Handle")
    .Where(sh => sh.SearchTerm.Contains(searchTerm) && sh.Timestamp > minDate && sh.Timestamp < maxDate);

прежде чем я попытался загрузить для любого Обработать searchHistories, которые соответствуют логике, но не знают, как использовать логику Include, которую вы опубликовали, поэтому в то же время я думаю, что обратный поиск будет не таким грязным решением

person Community    schedule 22.09.2009

Я знаю, что это может работать с одним включением. Никогда не тестируйте с двумя включениями, но попробовать стоит:

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"))
person Dherik    schedule 08.05.2015