I think your approach is the wrong way to use LINQ .
LINQ uses a deferred execution model for a specific reason. It allows you to combine a series of operations that are performed only when you tell it to calculate the result - often with .ToList() , .ToArray() , .First() - but you can also force the calculation by filtering with OrderBy , whose parameter is Func<T, ?> .
Now you are returning a List<Task> , which means that you forced the execution - what to do when you are ready to use the results - but if you then continue with further operations, they potentially load many more records into memory than you need.
You could do this:
public List<Task> GetTasksByAssignedTo<P>(Guid contactId, Func<Task, P> orderBy) { return dc.Tasks .Where(x => x.ContactId == contactId) .OrderBy(orderBy)
To execute the execution in the database, you need to modify it as follows:
public List<Task> GetTasksByAssignedTo<P>( Guid contactId, Expression<Func<Task, P>> orderBy) { return dc.Tasks .Where(x => x.ContactId == contactId) .OrderBy(orderBy) .ToList();
But the problem is that if you did this:
var query = from t1 in GetTasksByAssignedTo(contactId, t => t.Name) join t2 in GetTasksByAssignedTo(contactId, t => t.Name) on t1.Name equals t2.Name select new { t1, t2 };
Since your GetTasksByAssignedTo brings writes to memory, you are making a connection in memory. (Yes, the request is a little inventive, but the principle is solid, though.)
It is often much better to do this in a database.
Here's how to fix it:
public IQueryable<Task> GetTasksByAssignedTo<P>( Guid contactId, Expression<Func<Task, P>> orderBy) { return dc.Tasks .Where(x => x.ContactId == contactId) .OrderBy(orderBy); }
Now the above query will not be executed until you execute query.ToList() and everything will happen in the database.
But I have a bigger problem.
You hide a lot of information in GetTasksByAssignedTo . Someone using the code doesn't know that they really get the list when they read the code, and they really don't know if the real implementation is doing the right thing. I think for these types of queries it is often better to leave it as normal LINQ.
Compare these:
var tasks1 = GetTasksByAssignedTo(contactId); var tasks2 = GetTasksByAssignedTo(contactId, t => t.Name); var tasks3 = GetTasksByAssignedToDescending(contactId, t => t.Name); var tasks4 = ( from t in dc.Tasks where t.ContactId == contactId orderby t.Name descending select t ).ToList();
The first tasks1 request tasks1 not so bad, but it does not tell you what the return type is;
The second tasks2 request does something with some t and the Name property, but does not tell you that.
The third tasks3 query gives you a hint that it sorts in descending order, but does not tell you if this is a mysterious property of Name or something else.
The fourth tasks4 request tells you everything you need to know - it filters tasks on ContactId , the reverse order of the results on Name and finally returns a list.
Now take a look at this query:
var query2 = from t1 in dc.Tasks where t1.ContactId == contactId join t2 in dc.Tasks on t1.Name equals t2.Name where t2.ContactId != contactId orderby t2.Name descending select t2;
I can read it quite easily and see what it does. Imagine what the name of the helper method will be for this! Or what a crazy investment of helper methods would be required.
As a result, LINQ is an API for queries.
If you desperately want to create helper methods, use extension methods .
public static class TaskEx { public static IQueryable<Task> WhereAssignedTo(this IQueryable<Task> tasks, Guid contactId) { return tasks.Where(t => t.ContactId == contactId); } public static IQueryable<Task> OrderByName(this IQueryable<Task> tasks) { return tasks.OrderBy(t => t.Name); } }
This allows you to write the following:
var tasks = dc.Tasks .WhereAssignedTo(contactId) .OrderByName() .ToList();
And it is clear, concise, extensible, compound, reusable, and you control it during execution .