Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to construct Order By Expression dynamically in Entity Framework?

Tags:

I used the following methods to construct Order By Expression. Original Source

It is really slick. The downside is it only works if Property is string type.

How can I make it to accept different Property type without creating a bunch of methods for different data types?

public static bool PropertyExists<T>(string propertyName) {     return typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |       BindingFlags.Public | BindingFlags.Instance) != null; }  public static Expression<Func<T, string>> GetPropertyExpression<T>(string propertyName) {     if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |          BindingFlags.Public | BindingFlags.Instance) == null)     {         return null;     }      var paramterExpression = Expression.Parameter(typeof(T));      return (Expression<Func<T, string>>)Expression.Lambda(         Expression.PropertyOrField(paramterExpression, propertyName), paramterExpression); } 

Usage

// orderBy can be either Name or City. if (QueryHelper.PropertyExists<Club>(orderBy))  {     var orderByExpression = QueryHelper.GetPropertyExpression<Club>(orderBy);     clubQuery = clubQuery.OrderBy(orderByExpression);  }  else  {     clubQuery = clubQuery.OrderBy(c => c.Id);  }  

Problem

public class Club  {    public int Id { get; set; }    public string Name { get; set; }    public string City { get; set; }    public DateTime CreateDate { get; set; } <= this won't work }  

My Current Approach (Too many if statements)

public static Expression<Func<TSource, TKey>>      GetPropertyExpression<TSource, TKey>(string propertyName) {     if (typeof (TSource).GetProperty(propertyName, BindingFlags.IgnoreCase |          BindingFlags.Public | BindingFlags.Instance) == null)     {         return null;     }     var paramterExpression = Expression.Parameter(typeof (TSource));     return (Expression<Func<TSource, TKey>>)          Expression.Lambda(Expression.PropertyOrField(            paramterExpression, propertyName), paramterExpression); } 

The downside is I end up with a lot of if statements for each datatype.

if (QueryHelper.PropertyExists<Club>(orderBy))  {    if(orderBy == "CreateDate")    {              var orderByExpression = GetPropertyExpression<Club, DateTime>(orderBy);       ...    }    else if(orderBy == "Name" || orderBy == "City")    {       var orderByExpression = GetPropertyExpression<Club, string>(orderBy);       ...    }    ... } else  {     clubQuery = clubQuery.OrderBy(c => c.Id);  }  
like image 341
Win Avatar asked Jan 20 '16 17:01

Win


1 Answers

I found a solution with the help of Jon Skeet's old answer.

public static class QueryHelper {     private static readonly MethodInfo OrderByMethod =         typeof (Queryable).GetMethods().Single(method =>          method.Name == "OrderBy" && method.GetParameters().Length == 2);      private static readonly MethodInfo OrderByDescendingMethod =         typeof (Queryable).GetMethods().Single(method =>          method.Name == "OrderByDescending" && method.GetParameters().Length == 2);      public static bool PropertyExists<T>(this IQueryable<T> source, string propertyName)     {         return typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |             BindingFlags.Public | BindingFlags.Instance) != null;     }      public static IQueryable<T> OrderByProperty<T>(        this IQueryable<T> source, string propertyName)     {         if (typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |              BindingFlags.Public | BindingFlags.Instance) == null)         {             return null;         }         ParameterExpression paramterExpression = Expression.Parameter(typeof (T));         Expression orderByProperty = Expression.Property(paramterExpression, propertyName);         LambdaExpression lambda = Expression.Lambda(orderByProperty, paramterExpression);         MethodInfo genericMethod =            OrderByMethod.MakeGenericMethod(typeof (T), orderByProperty.Type);         object ret = genericMethod.Invoke(null, new object[] {source, lambda});         return (IQueryable<T>) ret;     }      public static IQueryable<T> OrderByPropertyDescending<T>(         this IQueryable<T> source, string propertyName)     {         if (typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |              BindingFlags.Public | BindingFlags.Instance) == null)         {             return null;         }         ParameterExpression paramterExpression = Expression.Parameter(typeof (T));         Expression orderByProperty = Expression.Property(paramterExpression, propertyName);         LambdaExpression lambda = Expression.Lambda(orderByProperty, paramterExpression);         MethodInfo genericMethod =            OrderByDescendingMethod.MakeGenericMethod(typeof (T), orderByProperty.Type);         object ret = genericMethod.Invoke(null, new object[] {source, lambda});         return (IQueryable<T>) ret;     } } 

Usage

string orderBy = "Name"; if (query.PropertyExists(orderBy)) {    query = query.OrderByProperty(orderBy);    - OR -     query = query.OrderByPropertyDescending(orderBy); } 
like image 66
Win Avatar answered Oct 15 '22 09:10

Win