Print Page | Close Window

HowTo: Dynamic IEntityQuery OrderBy

Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce 2009
Forum Discription: For .NET 3.5
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=1749
Printed Date: 25-Oct-2025 at 12:44pm


Topic: HowTo: Dynamic IEntityQuery OrderBy
Posted By: JoeGershgorin
Subject: HowTo: Dynamic IEntityQuery OrderBy
Date 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 });
    }   
}




Replies:
Posted By: JoeGershgorin
Date 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");
            }
        }
    }
}




Print Page | Close Window