How to delegate Queryable.Single calls in Custom LINQ provider
When creating custom LINQ provider , despite of looping query items, it is sometimes needed that we need to get the first , last, single or more specific items from the query result.
LINQ runtime creates Queryable instance of the query expression.Although, Queryable has a Single method defined , there is no such member in either IQueryable / IQueryProvider. Therefore, for the following query like
var query = from ph in context.Photos where ph.Id == PhotoId select ph; Photo photo = query.Single<Photo>();
We need to have a mechanism, that forwards the call to our custom defined Single method.
To easily , implement that , in LINQ.Flickr provider, I have created an Custom interface called IPhotoList ,that looks like
public interface IPhotoList<T> { ........... T Single(); T First(); T Last(); ............ ........... }
This interface is implemented in FlickrQuery, which also implementes IQueryable interface.
Now as for
Photo photo = query.Single<Photo>();
This method in FlickrQuery is called by system.
public object Execute(System.Linq.Expressions.Expression expression)
Therefore, the concept here, we will check if the expression is MethodCallExpression, if so , then we will further check if the return type is the one we are looking for , in this case Photo. Finally, we will need to reflect the method of the current class and compare that with the method for which the Execute is invoked, if both match, then we need to invoke the local equivalent for that and return the value back.
Code for that
MethodCallExpression mCallExp = (MethodCallExpression)expression; // when first , last or single is called if (mCallExp.Method.ReturnType == typeof(Photo)) { Type itemType = this.GetType(); string methodName = mCallExp.Method.Name; MethodInfo[] mInfos = itemType.GetMethods(); foreach (MethodInfo mInfo in mInfos) { if (string.Compare(methodName, mInfo.Name, false) == 0) { return itemType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, this, null); } } }
Hope this is useful,