I wrote a simple class for organizing entities based on a lambda expression at runtime.
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace DataModeling { public class QueryOrderer<TEntity> where TEntity : class { private LambdaExpression defaultSortExpression; private Dictionary<string, LambdaExpression> orderFieldLookup; public QueryOrderer() { orderFieldLookup = new Dictionary<string, LambdaExpression>(); } public void AddOrderMapping<TProp>(string fieldName, Expression<Func<TEntity, TProp>> selector) { orderFieldLookup[fieldName] = selector; } public void SetDefaultSortExpression<TProp>(Expression<Func<TEntity, TProp>> selector) { defaultSortExpression = selector; } public IQueryable<TEntity> GetOrderedEntries(string field, bool isDescending, IQueryable<TEntity> entries) { return orderEntries(entries, field, isDescending); } private IQueryable<TEntity> orderEntries(IQueryable<TEntity> entries, string fieldName, bool isDescending) { dynamic lambda = getOrderByLambda(entries, fieldName); if (lambda == null) { return entries; } if (isDescending) { return Queryable.OrderByDescending(entries, lambda); } else { return Queryable.OrderBy(entries, lambda); } } private dynamic getOrderByLambda(IQueryable<TEntity> entries, string fieldName) { if (!String.IsNullOrWhiteSpace(fieldName) && orderFieldLookup.ContainsKey(fieldName)) { return orderFieldLookup[fieldName]; } else { return defaultSortExpression; } } } }
You use this class by pre-setting all the fields:
QueryOrderer<User> orderer = new QueryOrderer<User>(); orderer.SetDefaultSortExpression(u => u.FullName); orderer.AddOrderMapping("UserId", u => u.UserId); orderer.AddOrderMapping("Name", u => u.FullName); orderer.AddOrderMapping("Email", u => u.Email); orderer.AddOrderMapping("CreatedOn", u => u.CreatedOn); ... var users = orderer.GetOrderedEntries("CreatedOn", isDescending: false, context.Users);
A good feature of this code is that it handles the displayed values ββvery well. For example, if you are trying to sort using a description rather than a key, you can use the external context when creating the sort expression:
orderer.AddOrderMapping("UserType", u => context.UserTypes .Where(t => t.UserTypeId == u.UserTypeId) .Select(t => t.Description) .FirstOrDefault());
The Entity Framework is smart enough to just collapse a sub request directly into an external request.