Подтвердить что ты не робот

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

как всегда, благодарен за любые советы или рекомендации

4b9b3361

Ответ 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"))