views:

134

answers:

1

I was just reading a recent question on using conditionals in Linq and it reminded me of an issue I have not been able to resolve. When building Linq to SQL queries programatically how can this be done when the number of conditionals is not known until runtime?

For instance in the code below the first clause creates an IQueryable that, if executed, would select all the tasks (called issues) in the database, the 2nd clause will refine that to just issues assigned to one department if one has been selected in a combobox (Which has it's selected item bound to the departmentToShow property).

How could I do this using the selectedItems collection instead?

IQueryable<Issue> issuesQuery;

// Will select all tasks
issuesQuery = from i in db.Issues
              orderby i.IssDueDate, i.IssUrgency
              select i;

// Filters out all other Departments if one is selected
   if (departmentToShow != "All")
   {
        issuesQuery = from i in issuesQuery
                      where i.IssDepartment == departmentToShow
                      select i;
    }

By the way, the above code is simplified, in the actual code there are about a dozen clauses that refine the query based on the users search and filter settings.

+4  A: 

If the number of conditions is unknown then it's easier to use lambda syntax instead of query comprehension, i.e.:

IQueryable<Issue> issues = db.Issues;
if (departmentToShow != "All")
{
    issues = issues.Where(i => i.IssDepartment == departmentToShow);
}
issues = issues.OrderBy(i => i.IssDueDate).ThenBy(i => i.IssUrgency);

(Assuming you want the ordering to happen after the filtering, which ought to be the case - I'm not sure if Linq will generate an optimized query if you try to do the ordering first).

If you've got a very large number of optional conditions then you can clean it up with predicates:

List<Predicate<Issue>> conditions = new List<Predicate<Issue>>();
if (departmentToShow != "All")
    conditions.Add(i => i.IssDepartment == departmentToShow);
if (someOtherThing)
    conditions.Add(anotherPredicate);
// etc. snip adding conditions

var issues = from i in issues
             where conditions.All(c => c(i))
             orderby i.IssDueDate, i.IssUrgency;

Or just use PredicateBuilder which is probably easier.

Aaronaught
Actually one item on my todo list is to pull the explicit ordering out and add that to the interface so the user has some control.
Mike B
In the Lambda expression above how would that work with departmentToShow being a collection from a combobox, I had thought that what you have is exactly the same as the query I have. I must confess I am a beginner and Lambdas are one of those things that make more sense to me in books than in practice.
Mike B
@Mike B: It's similar to what you have, but obviously less verbose and your original version does an ordering before the second filter (you want it to happen at the end). If `departmentToShow` is a collection then you write `departmentToShow.Contains(i.IssDepartment)` instead of `i.IssDepartment == departmentToShow`.
Aaronaught
Generally you can't use `var` for this scenario - it would have to be `IQueryable<SomeType>`.
Marc Gravell
That appears to be correct... intellisense works but compiler complains about implicit conversion. Interesting, I always thought `Table<T>` had an implicit conversion to `IQueryable<T>`. Corrected.
Aaronaught
Thanks for the awesome response, you answered my question and so much more. Once I understood how .Contains works and what a SQL IN statement does it all made sense. Since I was thinking Row.Contains(parameter) it seemed contains would not take a collection but in reality it is ParamaterCollection.Contains(Row) I see how I can use it.
Mike B