Dynamically build LINQ OrderBy
I have to start with a disclaimer. The content of this post is not original; in fact, while looking for the solution for my personal project, I found this jewel from the following sites:
First, create a OrderByHelper class as shown below.
Here is one example that shows how to use this extension. This function generates the list of staff members who report to a supervisor (specified in supervisor_id) and returns the list in json format. It sorts the list by title and then by last name.
All credits go to the respective bloggers. This post is just for me (or anyone who visit my blogger site) to see codes and samples.
First, create a OrderByHelper class as shown below.
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Web; namespace com.mycompany.intranet.Helpers { public static class OrderByHelper { // // Private data structure for this helper private enum SortDirection { Ascending = 0, Descending = 1 }; private class ItemInfo { public string PropertyName { get; set; } public SortDirection Direction { get; set; } public bool IsFirst { get; set; } } public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> enumerable, string param) { // 1. Convert IEnumerable to IQueryable using AsQueryable() // 2. Call OrderBy (return IQuerable - see below) // 3. Finally, convert IQueryable to IEnumerable return enumerable.AsQueryable().OrderBy(param).AsEnumerable(); } public static IQueryable<T> OrderBy<T>(this IQueryable<T> collection, string param) { foreach (ItemInfo item in ParseParam(param)) { collection = ApplyOrderBy<T>(collection, item); } return collection; } private static IQueryable<T> ApplyOrderBy<T>(IQueryable<T> collection, ItemInfo item) { string[] _props = item.PropertyName.Split('.'); // *** ??? Type _type = typeof(T); ParameterExpression _arg = Expression.Parameter(_type, "x"); Expression _expr = _arg; foreach(string _prop in _props) { // use reflection PropertyInfo _pi = _type.GetProperty(_prop); _expr = Expression.Property(_expr, _pi); _type = _pi.PropertyType; } Type _delegateType = typeof(Func<,>).MakeGenericType(typeof(T), _type); LambdaExpression _lambda = Expression.Lambda(_delegateType, _expr, _arg); string _methodName = string.Empty; if(!item.IsFirst && collection is IOrderedQueryable<T>) { if (item.Direction == SortDirection.Ascending) _methodName = "ThenBy"; else _methodName = "ThenByDescending"; } else { if (item.Direction == SortDirection.Ascending) _methodName = "OrderBy"; else _methodName = "OrderByDescending"; } // *** return (IOrderedQueryable<T>)typeof(Queryable).GetMethods().Single( _m => _m.Name == _methodName && _m.IsGenericMethodDefinition && _m.GetGenericArguments().Length == 2 && _m.GetParameters().Length == 2) .MakeGenericMethod(typeof(T), _type) .Invoke(null, new object[] {collection, _lambda} ); } private static IEnumerable<ItemInfo> ParseParam(string param) { if (string.IsNullOrEmpty(param)) yield break; // yield used inside IEnumerable string[] _items = param.Split(new char[]{ ',', ';' }); bool _isFirst = true; foreach(string _item in _items) { string[] _pair = _item.Trim().Split(' '); if(_pair.Length > 2) throw new ArgumentException(string.Format("Invalid OrderBy string '{0}'. Order By Format: Property [,|;] Property ASC [,|;] Property DESC", _item)); string _prop = _pair[0].Trim(); if (string.IsNullOrEmpty(_prop)) throw new ArgumentException("Invalid Property. Order By Format: Property [,|;] Property ASC [,|;] Property DESC"); SortDirection _direction = SortDirection.Ascending; if (_pair.Length == 2) _direction = ("desc".Equals(_pair[1].Trim(), StringComparison.OrdinalIgnoreCase)) ? SortDirection.Descending : SortDirection.Ascending; yield return new ItemInfo() { PropertyName = _prop, Direction = _direction, IsFirst = _isFirst }; _isFirst = false; } } } }
Here is one example that shows how to use this extension. This function generates the list of staff members who report to a supervisor (specified in supervisor_id) and returns the list in json format. It sorts the list by title and then by last name.
using
com.mycompany.intranet.Helpers;
public ActionResult GetDepartmentStaff(Guid supervisor_id)
{
var _staff = from p in db.People
join j1 in db.Jobs on p.Id equals j1.PersonId into pall
from j in pall.DefaultIfEmpty()
where j.ReportToPersonId == supervisor_id
where j.IsPrimary == true
where p.IsActive == true
select new
{
id = p.Id,
firstname = p.FirstName,
lastname = p.LastName,
title = p.Alias ?? p.JobTitle,
photo_url = @"/photos/" + p.PhotoFileName,
employee_id = p.EmployeeId.ToString(),
tel = p.Tel,
cell = p.Cell,
fax = p.Fax,
email = p.Email,
building = p.Bulding,
room = p.Room
};
return Json(_staff.OrderBy("title ASC;lastname ASC").ToList(),JsonRequestBehavior.AllowGet);
}
Comments
Post a Comment