Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modularize (refactor) Linq queries

Tags:

c#

linq

I have a few Linq queries. Semantically, they are

  1. a join b join c join d where filter1(a) && filter2(c) && filter3(d)

  2. a join b join c where filter1(a) && filter2(c)

  3. a join b join c join e where filter1(a) && filter2(c) && filter4(e)

...

I want to be able to factor out the shared part:

a join b join c where filter1(a) && filter2(c)

and dynamically append join d and filter3(d)

Is there a way to do this? I am already using the Predicate Builder to dynamically build conditionals (filters).

EDIT: I am using Linq-to-SQL.

EDIT: The base query looks like:

from a in As.AsExpandable()
join b in Bs on a.Id equals b.PId
join c in Cs on b.Id equals c.PId
where filter1(a) && filter2(b) && filter3(c)
select new A { ... }

filters are predicates in Predicate Builder. The type of the query is IQueryable<A>.

Next, I'd like to join this with d

from a in BaseQuery()
join d in D on a.Id equals d.PId

Currently join d .. causes a compilation error:

The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to Join

like image 745
Candy Chiu Avatar asked Oct 10 '22 04:10

Candy Chiu


1 Answers

Your example is a bit vague, but it is easy to create a method that returns an IQueryable<T> and reuse that method, if that’s what you mean. Here is an example:

// Reusable method
public IQueryable<SomeObject> GetSomeObjectsByFilter(Context c)
{
     return
         from someObject in context.SomeObjects
         where c.B.A.Amount < 1000
         where c.Roles.Contains(r => r.Name == "Admin")
         select someObject;
}

You can reuse this method in other places like this:

var q =
    from c in GetSomeObjectsByFilter(context)
    where !c.D.Contains(d => d.Items.Any(i => i.Value > 100))
    select c;

Because the way IQueryable works, only the final query (the collection that you start iterating) will trigger a call to the database, which allows you to build a highly maintainable system by reusing business logic that gets effectively executed inside the database, whiteout the loss of any performance.

I do this all the time and it improves the maintainability of my code big time. It works no matter which O/RM tool you run, because there is no difference in Queryable<T> composition, between writing the query in one peace, or splitting it out to different methods.

Note that you do sometimes need some smart transformations to get the duplicate parts in a single method. Things that might help are returning grouped sets, and returning a set of a different type, than what you think you need. This sounds a bit vaque, but just post a question here at SO when you have problems splitting up a method. There are enough people here that can help you with that.

like image 199
Steven Avatar answered Oct 28 '22 16:10

Steven