using System; using System.Data.SqlClient; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace NFine.Code { public static partial class ExtLinq { public static IOrderedQueryable SortBy(this IQueryable query, Expression> sortPredicate) where TEntity : class, new() { return InvokeSortBy(query, sortPredicate, SortOrder.Ascending); } public static IOrderedQueryable SortByDescending(this IQueryable query, Expression> sortPredicate) where TEntity : class, new() { return InvokeSortBy(query, sortPredicate, SortOrder.Descending); } private static IOrderedQueryable InvokeSortBy(IQueryable query, Expression> sortPredicate, SortOrder sortOrder) where TEntity : class, new() { var param = sortPredicate.Parameters[0]; string propertyName = null; Type propertyType = null; Expression bodyExpression = null; if (sortPredicate.Body is UnaryExpression) { var unaryExpression = sortPredicate.Body as UnaryExpression; bodyExpression = unaryExpression.Operand; } else if (sortPredicate.Body is MemberExpression) { bodyExpression = sortPredicate.Body; } else throw new ArgumentException(@"The body of the sort predicate expression should be either UnaryExpression or MemberExpression.", "sortPredicate"); var memberExpression = (MemberExpression)bodyExpression; propertyName = memberExpression.Member.Name; if (memberExpression.Member.MemberType == MemberTypes.Property) { var propertyInfo = memberExpression.Member as PropertyInfo; if (propertyInfo != null) propertyType = propertyInfo.PropertyType; } else throw new InvalidOperationException(@"Cannot evaluate the type of property since the member expression represented by the sort predicate expression does not contain a PropertyInfo object."); var funcType = typeof(Func<,>).MakeGenericType(typeof(TEntity), propertyType); var convertedExpression = Expression.Lambda(funcType, Expression.Convert(Expression.Property(param, propertyName), propertyType), param); var sortingMethods = typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static); var sortingMethodName = GetSortingMethodName(sortOrder); var sortingMethod = sortingMethods.First(sm => sm.Name == sortingMethodName && sm.GetParameters().Length == 2); return (IOrderedQueryable)sortingMethod .MakeGenericMethod(typeof(TEntity), propertyType) .Invoke(null, new object[] { query, convertedExpression }); } private static string GetSortingMethodName(SortOrder sortOrder) { switch (sortOrder) { case SortOrder.Ascending: return "OrderBy"; case SortOrder.Descending: return "OrderByDescending"; default: throw new ArgumentException("Sort Order must be specified as either Ascending or Descending.", "sortOrder"); } } } }