Andy Evans
Andy Evans

Reputation: 7176

Implementing conditional 'if' statements within a LINQ queries 'where' statement

I'm trying to figure out a way of querying an object in my datamodel and include only those parameters that are not null. Like below:

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;
}

Since the widgets table can get very large, I'd like to avoid doing this:

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;
}

I've looked at several example but don't really see anything that matches what I need to do.

Upvotes: 17

Views: 50678

Answers (5)

Muni Chittem
Muni Chittem

Reputation: 1126

we can use very simple way like below.

(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();
}

Upvotes: 1

xtofl
xtofl

Reputation: 41509

You're actually asking for a dispatcher within the linq query. The Where method takes a predicate, so you can build your predicate before creating the query.

-- EDIT -- at first, I thought it easier, wrote some pseudo code that didn't even compile. Now, howver, I think I got the point. This code will work; it separates building the where clause from applying it.

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

    }

and use this 'building' functionality like that:

    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);

I tested it with a little helper array:

        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);
        }

Upvotes: 1

curveship
curveship

Reputation: 331

Use an "or gate": preface every widget condition test with an "||" and a check to see if we're using that condition or not. If we're not, the second half of the "or" isn't evaluated. That's why it's a gate -- we don't go any further if the first part evaluates to true.

If I were writing it, I'd do it like below. I used the var syntatic sugar to hold LINQ query and moved the ToList() to the end.

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: grammar

Upvotes: 27

Viv
Viv

Reputation: 2595

How about something like this?

        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);

etc...?

Upvotes: 2

Fredrik M&#246;rk
Fredrik M&#246;rk

Reputation: 158289

What you want to avoid is actually executing the query until you are ready:

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();
}

Note how the ToList calls are removed. The query is not executed until you start iterating over it. Invoking ToList will force that to happen, so that the result can be put into a List<> and returned. I would even suggest to change the return value of the method to IEnumerable<Widget> and skipping the ToList call in the end:

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;
}

That way the calling code gets to decide when to execute the query (it may even add more conditions before doing so).

Upvotes: 35

Related Questions