Reputation: 7176
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
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
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
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
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
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