Внедрение условных операторов "if" в запросах LINQ "where"

Я пытаюсь выяснить способ запроса объекта в моем datamodel и включить только те параметры, которые не равны нулю. Как ниже:

public List<Widget> GetWidgets(string cond1, string cond2, string cond3)
{
    MyDataContext db = new MyDataContext();
    List<Widget> widgets = (from w in db.Widgets
                            where 
                                ... if cond1 != null w.condition1 == cond1 ...
                                ... if cond2 != null w.condition2 == cond2 ...
                                ... if cond3 != null w.condition3 == cond3 ...
                            select w).ToList();
    return widgets;
}

Так как таблица виджетов может стать очень большой, я бы хотел избежать этого:

public List<Widget> GetWidgets(string cond1, string cond2, string cond3)
{
    MyDataContext db = new MyDataContext();
    List<Widget> widgets = db.Widgets.ToList();

    if(cond1 != null)
        widgets = widgets.Where(w => w.condition1 == cond1).ToList();

    if(cond2 != null)
        widgets = widgets.Where(w => w.condition2 == cond2).ToList();

    if(cond3 != null)
        widgets = widgets.Where(w => w.condition3 == cond3).ToList();

    return widgets;
}

Я рассмотрел несколько примеров, но не вижу ничего, что соответствует тому, что мне нужно делать.

Ответы

Ответ 1

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

public List<Widget> GetWidgets(string cond1, string cond2, string cond3)
{
    MyDataContext db = new MyDataContext();
    var widgets = db.Widgets;

    if(cond1 != null)
        widgets = widgets.Where(w => w.condition1 == cond1);

    if(cond2 != null)
        widgets = widgets.Where(w => w.condition2 == cond2);

    if(cond3 != null)
        widgets = widgets.Where(w => w.condition3 == cond3);

    return widgets.ToList();
}

Обратите внимание, как удаляются вызовы ToList. Запрос не выполняется, пока вы не начнете повторять его. Вызов ToList заставит это произойти, так что результат может быть помещен в List<> и возвращен. Я даже предложил бы изменить возвращаемое значение метода на IEnumerable<Widget> и пропустить вызов ToList в конце:

public IEnumerable<Widget> GetWidgets(string cond1, string cond2, string cond3)
{
    MyDataContext db = new MyDataContext();
    var widgets = db.Widgets;

    if(cond1 != null)
        widgets = widgets.Where(w => w.condition1 == cond1);

   // [...]

    return widgets;
}

Таким образом, вызывающий код принимает решение о выполнении запроса (он может даже добавить дополнительные условия перед этим).

Ответ 2

Используйте "или ворота": предисловие каждого теста состояния виджета с помощью "||" и проверить, используем ли мы это условие или нет. Если мы этого не сделаем, вторая половина "или" не будет оценена. Вот почему это ворота - мы не продвигаемся дальше, если первая часть оценивает значение true.

Если бы я писал это, я бы сделал это, как показано ниже. Я использовал синтаксический сахар var для хранения запроса LINQ и переместил ToList() в конец.

public List<Widget> GetWidgets(string cond1, string cond2, string cond3) 
{ 
    MyDataContext db = new MyDataContext(); 
    var widgets = from w in db.Widgets 
                  where (cond1 == null || w.condition1 == cond1)
                     && (cond2 == null || w.condition2 == cond2)
                     && (cond3 == null || w.condition3 == cond3)
                  select w;
    return widgets.ToList();
} 

edit: грамматика

Ответ 3

Как насчет чего-то подобного?

        IEnumerable<Widget> condQuery = (from w in db.Widgets);
        if(cond1 != null ) condQuery = condQuery.Where(w=> w.condition1 == cond1);
        if(cond2 != null ) condQuery = condQuery.Where(w=> w.condition2 == cond2);

и т.д...?

Ответ 4

Вы действительно запрашиваете диспетчера в запросе linq. Метод Where принимает предикат, поэтому вы можете создать предикат перед созданием запроса.

- EDIT - сначала я подумал, что это проще, написал какой-то псевдокод, который даже не компилировался. Теперь, как бы я ни думал, я понял. Этот код будет работать; он отделяет создание предложения where от его применения.

    static Predicate<Widget> combine( 
           Predicate<Widget> existing, 
           Predicate<Widget> condition )
    {
        var newpred = new Predicate<Widget>( w=> existing(w) && condition(w) );
        return newpred;

    }

и используйте эту функциональность "building":

    static void Main(string[] args)
    {
        string cond1 = "hi";
        string cond2 = "lo";
        string cond3 = null;
        var pr = new Predicate<Widget>( (Widget w ) => true );
        if (cond1 != null) pr = combine( pr, w => w.condition1 == cond1);
        if (cond2 != null) pr = combine( pr, w => w.condition2 == cond2);
        if (cond3 != null) pr = combine( pr, w => w.condition3 == cond3);

Я тестировал его с помощью небольшого вспомогательного массива:

        var widgets = new Widget[]{
            new Widget (){ condition1 = "" },
            new Widget (){ condition1 = "hi", condition2 = "lo" }
        };

        var selected = widgets.Where( (w) => pr(w));

        foreach (var w in selected) {
            Console.WriteLine(w);
        }

Ответ 5

мы можем использовать очень простой способ, как показано ниже.

(from e in employee
join d in departments on e.departmentId equals d.departmentId
Select new {
e.name,
d.name,
getEmployeeContacts(e)
}
//return active contact if not return first . This is same like if else along with null check
private contact getEmployeeContacts(Employee e )
{
 return e.Contacts.FirstOrDefault(x => x.Active == 1) ?? e.Contacts.FirstOrDefault();
}