PredicateBuilder и вложенные предикаты

Я пытаюсь использовать предикат из Albahari для создания инструкции TSQL, например:

select * from channel
where channel.VendorID IN (@vendorIDs)
AND channel.FranchiseID IN (@franchiseIDs)

или предикат вроде: c => (c.VendorID = x || c.VendorID == x2 ...) && (c.FranchiseID == f || c.FranchiseID == f2 ...)

но у меня проблемы. Вот моя попытка:

    var vendorPredicate = PredicateBuilder.False<Channel>();
    foreach (Vendor vendor in workOrderSessionData.SelectedVendors)
    {
        int tempId = vendor.VendorID;
        vendorPredicate = vendorPredicate.Or(c => c.VendorID == tempId);
    }

    var franchisePredicate = PredicateBuilder.False<Channel>();
    foreach (Franchise franchise in workOrderSessionData.SelectedFranchises)
    {
        int tempId = franchise.FranchiseID;
        franchisePredicate = franchisePredicate.Or(c => c.FranchiseID == tempId);
        // doesn't work franchisePredicate.Or(vendorPredicate);
    }

Channel.SelectByPredicate(franchisePredicate);

В моей таблице 60 000 строк, поэтому перейти к базе данных и выбрать их все, а затем фильтрация невозможна. Канал — это объект LinqToSql. Также либо SelectedFranchises, либо SelectedVendors могут быть пустыми, но не оба одновременно. РЕДАКТИРОВАТЬ: мне нужно также разделить этот список по каналу. Франчайзинг. Имя. Может быть, мне следует просто использовать хранимую процедуру?

Как бы вы это сделали?


person AlignedDev    schedule 19.11.2009    source источник


Ответы (3)


Чтобы обойти всю проблему с предикатами, почему бы не использовать оператор «Contains()»?

IE

var myResults =
    from c in Channel
    where
        workOrderSessionData.SelectedVendors.Select(sv => 
            sv.VendorID).Contains(c.VendorID)

        && workOrderSessionData.SelectedFranchises.Select(sf => 
            sf.FranchiseID).Contains(c.FranchiseID)
    select c;
Channel.SelectByPredicate(franchisePredicate);

В качестве альтернативы, чтобы использовать метод предиката, вы не захотите соединять их с помощью «или», потому что два условия являются «и» в вашем примере оператора SQL. Вместо этого просто запустите их последовательно через предложение where. Я не знаю, как работает ваша функция SelectByPredicate, но вы можете добиться успеха, следуя тому же шаблону:

var myResults = Channel.SelectByPredicate(franchisePredicate);
myResults = myResults.SelectByPredicate(vendorPredicate);

var myResults = Channel.Where(franchisePredicate).Where(vendorPredicate);

Обновление из обсуждения в комментариях

Если вы хотите, чтобы только идентификатор поставщика/идентификатор франшизы соответствовал, если workordersessiondata содержит хотя бы один из этих идентификаторов, вы должны использовать следующую логику:

List<int> VendorIDs = workOrderSessionData.SelectedVendors.Select(sv => 
    sv.VendorID).ToList();

List<int> FranchiseIDs = workOrderSessionData.SelectedFranchises.Select(sf => 
    sf.FranchiseID).ToList();

var myResults = Channel;

if(VendorIDs.Count > 0)
    myResults = MyResults.Where(c => VendorIDs.Contains(c.VendorID));

if(FranchiseIDs.Count > 0)
    myResults = MyResults.Where(c => FranchiseIDs.Contains(c.FranchiseID));
person Michael La Voie    schedule 19.11.2009
comment
первый пример не работает, если SelectedVendors пуст или SelectedFranchises пуст (что возможно). Как бы вы справились с этим? Предложение where из Sql Profilier: WHERE 0 = 1 - person AlignedDev; 19.11.2009
comment
Под неудачами вы подразумеваете, что он возвращает 0 строк или выдает исключение? - person Michael La Voie; 19.11.2009
comment
под неудачами я подразумеваю пустой результат из базы данных. Спасибо за помощь. - person AlignedDev; 19.11.2009
comment
Не проблема. Причина, по которой я спрашиваю, заключается в том, что предоставленный вами SQL-запрос также не вернет результатов, если какой-либо из этих операторов IN завершится ошибкой, потому что в нем говорится, что оба идентификатора должны быть в соответствующих списках. Похоже, вы просите об этом, за исключением случаев, когда один из списков пуст, тогда вы хотите сопоставить только полный список. Это правильно? - person Michael La Voie; 19.11.2009
comment
Когда один из списков пуст, мне все равно нужно фильтровать на основе другого заполненного списка. Поэтому мне нужно получить все каналы по поставщикам и франшизам или все каналы для поставщиков или все каналы для франшиз. - person AlignedDev; 19.11.2009

Ну, я только что сделал это:

var prope = outerWhere.ToString();
if (prope.Equals("f => True") == false || prope.Equals("f => False") == false)
  query = query.Where(outerWhere);

Может быть, самая чистая из всех версий...

person Yves M.    schedule 23.07.2010

Учитывая расширения:

public static class PredicateExtensions
{
    public static Predicate<T> Or<T>(this Predicate<T> @this, Predicate<T> or) {
        return value => @this(value) || or(value);
    }

    public static Predicate<T> And<T>(this Predicate<T> @this, Predicate<T> and) {
        return value => @this(value) && and(value);
    }
}

Вы можете сделать это:

    Predicate<Channel> vendorPredicate = c => false;

    foreach (var vendor in workOrderSessionData.SelectedVendors)
    {
        int tempId = vendor.VendorID;       
        vendorPredicate = vendorPredicate.Or(c => c.VendorID == tempId);
    }

    Predicate<Channel> franchisePredicate = c => false;
    foreach (var franchise in workOrderSessionData.SelectedFranchises)
    {
        int tempid = franchise.FranchiseID;
        franchisePredicate = franchisePredicate.Or(c => c.FranchiseID == tempid);
    }

    var channelPredicate = vendorPredicate.And(franchisePredicate);

    var query = Channels.Where (c => channelPredicate(c));
}
person Marcel Valdez Orozco    schedule 10.04.2012