Как я могу создать динамическое выражение выбора в LINQ? - PullRequest
3 голосов
/ 16 июля 2010

То, что у меня сейчас есть, выглядит примерно так:

if(userLikesBananas)
{
    return from fruit in basket
           select new Fruit
           {
               AteBanana = Bowl.Any(b => b.OwnedBy == user && b.Contains(fruit) && fruit.Type == FruitType.Banana),
               ...
               ...
               //lots of properties
               ...
           }
}
else
{
    return from fruit in basket
           select new Fruit
           {
               AteBanana = Bowl.Any(b => b.Contains(fruit)),
               ...
               ...
               //lots of properties
               ...
           }
}

По общему признанию, пример не имеет абсолютно никакого смысла, но принцип в том, что я хочу изменить условия выбора свойств на основе произвольных критериев. Прямо сейчас операторы выбора повторяются.

Теперь пришло время, когда мне нужно добавить критерии, зависящие от другого. Я не хочу иметь 4 разных случая, когда условия собственности немного отличаются.

То, что я хочу сделать, выглядит примерно так:

Func<Fruit, bool> fruitFunc = f => false;

if(userLikesBananas)
{
    fruitFunc = f => Bowl.Any(b => b.OwnedBy == user && b.Contains(f) && f.Type == FruitType.Banana);
}
else
{
    fruitFunc = f => Bowl.Any(b => b.Contains(f));
}

return from fruit in basket
       select new Fruit
       {
           AteBanana = fruitFunc(fruit)
           ...
           ...
           //lots of properties
           ...
       };

Проблема в том, что выражение нельзя преобразовать в sql, поскольку оно содержит динамический вызов. Я попытался обернуть Func в Expression, но, похоже, возникает та же проблема.

Итак, вопрос в том, как избежать копирования и вставки?

Ответы [ 2 ]

3 голосов
/ 21 января 2012

... мой английский не очень хорош, но я постараюсь объяснить, как легко решить эту проблему: -)

Динамический Linq плох для управления типами - он прост в использовании, но вы можетене просматривайте получившийся объект (x.Name, x.Surname и т. д.)

Существует (немного похожий на noob) трюк для решения этой проблемы (я использую его и его работоспособность):

  1. Создать enum с атрибутами выбранного вами объекта.

    public enum MyAtrs {ID, FirstName, фамилия}

  2. создать Dictionary<MyAtrs,bool> (и заполнить его) для условий (установите true, если вы хотите получить это свойство)

    public Dictionary Dic = new Dictionary ();Dic.Add (MyAtrs.ID, правда);Dic.Add (MyAtrs.Firstname, ложь);Dic.Add (MyAtrs.Surname, true);

  3. Встроенный запрос:

    var query = DBContext.MyDBTable.Where (предикат) .Select (e =>new {ID = Dic [MyAtrs.ID]? e.dbID: 0, имя = Dic [MyAtrs.Firstname]? e.dbFirstname: null, фамилия = Dic [MyAtrs.Surname]? e.dbSurname: null,});

В этом случае в операторе SQL Select будут все 3 столбца, но это всего лишь несколько байтов (кто имеет значение ...).SQL Server возвращает вам все 3 столбца, но (в этом случае) Имя будет пустым (что-то вроде {ID = 123, Имя =, Фамилия = "Работа"}).Это не блестяще, но это простой способ, как создать «динамическое» выражение выбора без потери контроля над типами:)

0 голосов
/ 16 июля 2010

Я могу предложить написать Func по-другому (может быть, что-то вроде этого):

fruitFunc = f => Bowl.Any(b => ((f.Type == FruitType.Banana && b.OwnedBy == user && userLikesBananas) || !userLikesBananas) && b.Contains(f));

Я не проверял, работает ли он, но это может быть способом написания функции для покрытия большего количества случаев (не только 2). Этот подход как-то похож на булеву алгебру, насколько я помню ...

Приветствия ...

...