I'm trying to improve the performance for some of our LINQ queries and there is one little thing that has a lot of space for improvement: joins. Almost all my queries have some join that is used to filter the result, but are not selected into de result. And these filtering conditions are optional...
What I have today is something like:
var q = from t1 in context.Set<T1>()
where t1.mandatoryfilter >= 0
select t1;
if (useFilter)
{
var q2 = from t1 in q
from t2 in context.Set<T2>().Where(t2 => t2.fk == t1.pk).DefaultIfEmpty
where t2.filterProperty == filterValue
select t1;
if (useFilter2)
{
[...]
return q3.ToList();
}
return q2.ToList();
}
else
{
if (useFilter2)
{
[...]
return q2.ToList();
}
return q.ToList();
}
This will generate a projection of the query. And depending on the complexity/quantity of the filters it will generate more and more projections and the code need to be nested if I need to combine some of the filters. The resulting query can grow to a size where it gets too large to send over the internet (not actually too large XD but it becomes a performance issue) and the code itself gets hard to maintain.
I know I could just change back to string SQL... but that is not a very elegant solution is it?
I'd like to know if there is a way to inject the join and the filter directly do the Expression Tree, so the provider would not generate the projections and the code would be linear and simple.
ps.: very important information, I'm using EF6, Linq-to-Entities, default SqlClient.
Thank you all in advance.
If you use navigation properties you hardly ever need joins and you can apply most filter conditions as a single expression.
Let's suppose your
T1
entity has a navigation propertyT2
. The filter expression will look like this:You also see here that you can just append an expression to the existing query
q
.Now it turns into a commonly applied pattern to add filter conditions to an
IQueryable
:(Note that
query
should be cast toIQueryable
, otherwise the assignmentsquery = query....
don't compile.)And now you can even apply filters to 1:n associations
You can't do this if you use
join
for filtering, because it would multiply the result set.I'm not too confident with the query-syntax version of LINQ, and don't have VS open, so I'm using the method-syntax to make sure I get it correct: but the same idea applies.
This is a fairly standard way of applying filters. You don't create new queries each time, you simply append a predicate for each filter. Works for joins too (and unless you're using a custom join you likely don't need to explicitly use
.Join
)