Mehfuz's WebLog

Live crazy, think different!

Sponsors

News

Passionate about cutting edge technologies and facinated by the modern web and phone revolution.Currently working at Telerik Corporation, the leading .net component vendor.
Follow me


Articles


Projects

LinqExtender 1.4.2 - Supporting Complex type arguments

I just made a quick update to the existing release of LinqExtender. I recently found a bug while building a feature for FlickrXplorer is that if you use constant type query with orderby clause it does pretty well, but it simply does not do well with complex ones. I have used the same logic that I have used for where clause arguments. Also, it will be out of the scope for this post to drill it down all the LinqExtender logic fort hat. But I would put a brief overview on it that can help you out while building your own IQueryable implementation.

If I didn't mentioned in my earlier posts. LinqExtender supports two way orderby in your implemented provider

First by the object property  while looks like

var query =  from q in bookContext
             orderby q.LastUpdated descending
             select q;

In this case, Bucket.OrderByClause will be containing the detail of LastUpdated property and its relation to sorting.

Now, it is also possible to do orderby in the following way

var query =  from q in bookContext
             orderby "LastUpdated" descending
             select q;

But what about the query with member access. Let's assume that there is a Instance class and inside there is one method which has a local variable that is a Enum and that changes by user input. Finally, it is passed in a LINQ query.

PhotoOrder order = PhotoOrder.Interestingness;
// more code to change it on user provided order by

var query = (from ph in context.Photos
             where ph.PhotoSize == PhotoSize.Square
                   && ph.SearchMode == SearchMode.FreeText
                   && ph.FilterMode == FilterMode.Safe
                   && ph.Extras == (ExtrasOption.Views | ExtrasOption.Tags)
                   && ph.SearchText == key
             orderby order descending
             select ph).Take(pageLen).Skip(index);

Now, how to parse this or if any sorts of member access is used ? As we know that LINQ expressions are first translated to MethodCallExpression.In case for  orderby the MethodCallExpression.Method.Name name equals "OrderBy". Once you found a way to get around it. You can then easily build up an extension method or in my case, I have created an extension method that dynamically gets the value out of the method call argruments.

public static object GetValueFromExpression(this Expression expression)
{
    object value = null;

    UnaryExpression unaryExpression = GetUnaryExpressionFromMethodCall(expression);
    LambdaExpression lambdaExpression = unaryExpression.Operand as LambdaExpression;
    // get the value by dynamic invocation, used for getting value for MemberType expression.
    value = Expression.Lambda(lambdaExpression.Body).Compile().DynamicInvoke();
    return value;
}

As with the hierarchy expression starts with Unary then goes to Lamda and further down. Once we have the lamda expresion , we can then compile it to method call Delegate , which gives  a nice method named DynamicInvoke that brings out the result. In the code, one more thing that I have done is the extraction of the MethodCallExpression out of UnaryExpression which brings the following code block

public static UnaryExpression GetUnaryExpressionFromMethodCall(Expression expression)
{
    MethodCallExpression mCall = expression as MethodCallExpression;
    UnaryExpression uExp = null;

    foreach (Expression exp in mCall.Arguments)
    {
        if (exp is UnaryExpression)
        {
            uExp = exp as UnaryExpression;
            break;
        }
        else if (exp is MethodCallExpression)
        {
            uExp = GetUnaryExpressionFromMethodCall(exp);
            break;
        }
    }
    return uExp;
}

This simple code block reveals one more thing that each method call can be nested and as such for nested case we need to go to the further leaf and fetch the value.So far, this is really useful to me for parsing query expressions with complex member access arguments. But it also gives a way, if you are planning your own IQueryable implementation.

Moving forward, while working with LinqExtender toolkit, if you like attribute oriented programming, you need to specify and get attributes on objects despite the one defined by the toolkit. That follows

  1. LinqVisibleAttrible (Marks a property to be processed by LinqExtender when used in a query)
  2. OriginalFieldNameAttribute (Overrides the property name, if data store has a different name)
  3. OriginalEnitityNameAttribute (Overrides the class name)
  4. UniqueIdentifierAttribtue (Marks a property Unique which is useful for updating object, please see my earlier post).

So what if you want to have your own and get it in the AddItem, UpdateItem, RemoveItem or Process overrides ? You can do it easily by the following statement.

// let's say during the add i want to track MyAttribute
protected override bool AddItem(Bucket bucket)
{
    MyAttribute attribute = (MyAttribute)item.FindAttribute(typeof(MyAttribute)); 
}

This is all for now. Before I end, I would like to bring down a brief history how I started LinqExtender. Now, there are pretty good amount of LINQ providers out there and still growing and my motto is to help them get started with no pain.When I stared LinqExtender it came with a handful of features which has grown efficient day by day with the help of community. I build everything that people requests me and sounds logical to roadmap of the project. In the end , the basic concept remains the same from day one which is, "build more LINQ providers with less effort and it takes the most complexity on its own without scarifying features (Ex. projection, complex query parsing, etc)". It is basically suited for cloud APIS  like one I have built named Athena (Formarly:LINQ.Flickr) but can be used with ORMs and core data objects to provide custom LINQ support over legacy interface.

Hope that helps

kick it on DotNetKicks.com

Posted: Oct 25 2008, 07:24 PM by mehfuzh | with 1 comment(s) |
Filed under: , ,

Comments

No Comments