I want to call an IQueryable<T>
method from my repository, but I want to eagerly load child objects in my ORM. To keep the persistence logic in the persistence layer, I want to pass a list of expressions representing the properties I want to eagerly load.
My IRepository<TClass>
method looks like this:
IQueryable<TClass> AsQueryable();
I would like to make it look like :
IQueryable<TClass> AsQueryable(params --something here--[] includeProperties);
...so that I can call it like:
var q = myFooRepository.AsQueryable(x => x.Property1, x => x.Property2, ...);
and disassemble the delegate on the back end to eagerly load the specified property(ies).
What should I be using for this?
What I came up with assumes that you need to have all your properties to be of the same type (represented using generic parameter O in the following sample code):
hope it'll help. I'm still thinking how I can make the be different. nice question! actually you don't need to use System.Linq.Expression.Expression> but just Func.
These options seem like they shouldn't be the concern of this method, since you probably want your entire repo to either load properties lazily or eagerly. I presume you have other methods in this interface, apart from
AsQueryable
?A different approach would therefore be to leave this functionality out of the
AsQueryable()
method, but instead create additional methods which would create a new wrapper configured to load eagerly (I wouldn't mutate the underlying instance).I.e.
This is neat because you don't have to let your caller code decide what to load eagerly, and most likely reduces the amount of plumbing code written all over the place. This way you create the eager repo and pass it forward to a bunch of unsuspecting code.
Your
AsQueryable<TClass>
should have property expressions as parameters and it would need to have the following signature:Note that we are using
Func<TClass, object>
which is a function acepting TClass as input and returning an object. That allows us to make a call as follows:Also note that I did not choose an
object
as the TResult ofFunc<TClass, object>
by chance. Since the TResult generic parameter of the function delegate is covariant, that allows us to pass expressions even with different property types. So your Property1 and Property2 in the example above do not need to be of same type.That would be it, regarding your question I guess, but here is a little extra:
If you by chance need to evaluate the passed expressions in order to use them with your ORM (e.g. you just need property names but you want to pass them as expressions in order to avoid hardocoding names and preserve compile-time checks), you would need something like this:
The code above ensures that the passed expressions are property expressions and it extracts PropertyInfo from it.