New Posts New Posts RSS Feed: HowTo: Dynamic IEntityQuery OrderBy
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

HowTo: Dynamic IEntityQuery OrderBy

 Post Reply Post Reply
Author
JoeGershgorin View Drop Down
Newbie
Newbie


Joined: 24-Feb-2010
Location: USA
Posts: 14
Post Options Post Options   Quote JoeGershgorin Quote  Post ReplyReply Direct Link To This Post Topic: HowTo: Dynamic IEntityQuery OrderBy
    Posted: 26-Apr-2010 at 1:57am
Just thought I'd share some code adapted from online sources that others may find useful.

I had a need to specify an OrderBy for an IEntityQuery<T> server side, server side since I also need to page results, and obviously the sort order has an effect on the actual result set when dealing with paged data. The issue was that the OrderBy property path I needed is not known at compile time so I needed a solution that allowed me to specify the OrderBy property path by string value. The following is some helper extension methods to allow just that.

So for instance instead of this:


leads = leads.OrderBy(lead => lead.Person.FirstName);


You could do something like this:

var orderBypath = "Person.FirstName";
leads = leads.OrderBy(orderBypath);


Here's the code:


public static class EntityQueryExtensions
{
    public static IEntityQuery<TSource> Order<TSource>(this IQueryable<TSource> source, string property, ListSortDirection listSortDirection)
    {
        if (listSortDirection == ListSortDirection.Ascending)
        {
            return ApplyOrder(source, property, "OrderBy");
        }
       
        // Descending           
        return ApplyOrder(source, property, "OrderByDescending");                      
    }

    public static IEntityQuery<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string property)
    {
        return ApplyOrder(source, property, "OrderBy");
    }

    public static IEntityQuery<TSource> OrderByDescending<TSource>(this IQueryable<TSource> source, string property)
    {
        return ApplyOrder(source, property, "OrderByDescending");
    }

    public static IEntityQuery<TSource> ThenBy<TSource>(this IQueryable<TSource> source, string property)
    {
        return ApplyOrder(source, property, "ThenBy");
    }

    public static IEntityQuery<TSource> ThenByDescending<TSource>(this IQueryable<TSource> source, string property)
    {
        return ApplyOrder(source, property, "ThenByDescending");
    }      

    public static IEntityQuery<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
    {
        ParameterExpression arg = Expression.Parameter(typeof(T), property);
        Expression expr = arg;

        foreach (string prop in property.Split('.'))
        {
            // use reflection (not ComponentModel) to mirror LINQ
            expr = Expression.PropertyOrField(expr, prop);
        }

        Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), expr.Type);
        LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);

        return (IEntityQuery<T>)

        typeof(Queryable).GetMethods().Single(
            method => method.Name == methodName &&
                      method.IsGenericMethodDefinition &&
                      method.GetGenericArguments().Length == 2 &&
                      method.GetParameters().Length == 2).MakeGenericMethod(typeof(T), expr.Type)
                      .Invoke(null, new object[] { source, lambda });
    }   
}

Back to Top
JoeGershgorin View Drop Down
Newbie
Newbie


Joined: 24-Feb-2010
Location: USA
Posts: 14
Post Options Post Options   Quote JoeGershgorin Quote  Post ReplyReply Direct Link To This Post Posted: 11-May-2010 at 12:28am
Here's a few more extension methods I recently created that others may find useful:


public static class EntityQueryExtensions
{

// Transform IEntityQuery<TSource> into a count query
public static IEntityQuery<int> AsQueryCount<TSource>(this IEntityQuery<TSource> source)
{
    return (EntityQuery<int>) source.GroupBy(g => 1).Select(c => c.Count());
}

// Transform IEntityQuery into a count query
public static IEntityQuery<int> AsQueryCount(this IEntityQuery source)
{
    // Can't GroupBy/Select an untyped IEntityQuery
    // Can't cast source IEntityQuery to IEntityQuery<Entity> or
    // even IEntityQuery<object>
    //
    // So we're left with using reflection to get the queryable type
    // and calling the typed version of the AsQueryCount static extension
    // method.
    //
    // Note: If someone can come up with a solution to do this without
    // reflection let me know!
    MethodInfo methodInfo;
    ParameterInfo[] parameters;

    Type entityType = source.QueryableType;

    ReflectionHelpers.FindStaticMethod(typeof (EntityQueryExtensions), "AsQueryCount", new[] {entityType},
                                       new[] {entityType}, out methodInfo, out parameters);

    object result = methodInfo.Invoke(null, new object[] {source});

    return result as IEntityQuery<int>;
}

}





public static class ReflectionHelpers
{
    public static void FindStaticMethod(Type type, string methodName, Type[] typeArguments, Type[] parameterTypes,
                                        out MethodInfo methodInfo,
                                        out ParameterInfo[] parameters)
    {
        methodInfo = null;
        parameters = null;

        if (null == parameterTypes)
        {
            methodInfo = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);
            methodInfo = methodInfo.MakeGenericMethod(typeArguments);
            parameters = methodInfo.GetParameters();
        }
        else
        {
            // Method is probably overloaded. As far as i know there's no other way to get the MethodInfo instance, we have to
            // search for it in all the type methods
            MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
            foreach (MethodInfo method in methods)
            {
                if (method.Name == methodName)
                {
                    // create the generic method
                    MethodInfo genericMethod = method.MakeGenericMethod(typeArguments);
                    parameters = genericMethod.GetParameters();

                    // compare the method parameters
                    if (parameters.Length == parameterTypes.Length)
                    {
                        for (int i = 0; i < parameters.Length; i++)
                        {
                            if (parameters.ParameterType != parameterTypes)
                            {
                                continue; // this is not the method we'r looking for
                            }
                        }

                        // if we'r here, we got the rigth method
                        methodInfo = genericMethod;
                        break;
                    }
                }
            }

            if (null == methodInfo)
            {
                throw new InvalidOperationException("Method not found");
            }
        }
    }
}



Edited by JoeGershgorin - 11-May-2010 at 12:31am
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down