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,

kick it on DotNetKicks.com

2 Comments

  • Photo photo = (from ph in context.Photos where ph.Id == PhotoId select ph).Single&lt;Photo&gt;();
    or
    Photo photo = context.Photos.Where&lt;Photo&gt;(ph =&gt; ph.Id == PhotoId).Single&lt;Photo&gt;();

  • That's right , either for

    Photo photo = (from ph in context.Photos where ph.Id == PhotoId select ph).Single();

    or

    Photo photo = context.Photos.Where(ph => ph.Id == PhotoId).Single();

    the expresstion tree will be the same, therefore, it will get the same result.

Comments have been disabled for this content.