Just return an IQueryable<T>
.
Before you write another bit of "repository code", you will benefit significantly from reading Ayende's article Architecting in the Pit of Doom - The Evils of the Repository Abstraction Layer
Your approach is, without a doubt, adding significant unnecessary complexity.
All of the code from the other question at Generic List of OrderBy Lambda fails to do anything other than mask an existing effective API with an unnecessary and unfamiliar abstraction.
Regarding your two concerns,
LINQ providers do behave differently but as long as the predicates that you are passing can be processed by the LINQ provider, this is irrelevant. Otherwise, you will still encounter the same issue, because you are passing in an Expression
, which gets passed to the IQueryable
eventually anyway. If the IQueryProvider
implementation can't handle your predicate, then it can't handle your predicate. (You can always call a ToList()
if you need to evaluate prior to further filtering that cannot be translated).
Modifying a query can cause performance issues, but it is more likely to expose much needed functionality. Furthermore, the performance issues incurred by a sub-optimal LINQ query are likely to be significantly less detrimental than the performance issues incurred by pulling a lot more records than you need in order to avoid exposing an IQueryable
or by systematically filtering any data access logic through bloated levels of abstractions that don't actually do anything (the first threat is more significant). In general, this won't be an issue because most leading LINQ providers will optimize your query logic in the translation process.
If you want to hide your query logic from the front end, then don't try making a generic repository. Encapsulate the queries with actual business specific methods. Now, I may be mistaken, but I am assuming your use of the repository pattern is inspired by Domain Driven Design. If this is the case, then the reason for using a repository is to allow you to create a persistence-ignorant domain with a primary focus on the domain model. However, using this kind of a generic repository doesn't do much more than change your semantics from Create Read Update Delete
to Find Add Remove Save
. There isn't any real business knowledge embedded there.
Consider the meaningfulness (and usability) of an
interface IPersonRepository
{
Person GetById(int id);
IEnumerable<Person> FindByName(string firstName, string lastName);
}
in contrast to
interface IRepository<T> {
IEnumerable<T> FindBy(Query<T> query);
}
Furthermore, can you actually point to a benefit to using the IRepository<T>
at all (as opposed to an IQueryable<T>
)?
Also, consider that with the generic approach, you are not actually encapsulating query logic at all. You end up building it externally, which is going to lead to more additional unnecessary code.
*One other note about resources that advise against using IQueryable<T>
, is that it is worthwhile to look at their publication date. There was a time when the availability of LINQ providers were pretty limited (to early EF and LINQ-to-SQL). At that time exposing an IQueryable<T>
would entail incompatibility with some of Microsoft ORM's more popular substitutes (LINQ-to-NHibernate has long since been implemented). At this point in time, LINQ support is practically ubiquitous in serious ORM .NET libraries
finder
lambda expressions and it works fine. One other thing I did was to expose a QuerySpec class that allows the user to spec thefinder
, theorderbys
plusskip
andtake
. – Bandung